;       SCCSID = @(#)subr.asm   6.3 91/11/05
PAGE    ,132
TITLE   SUBR - General Kernel Subroutines
NAME    SUBR

SCCSID  equ     "@(#)subr.asm   6.3 91/11/05"

;**     SUBR.ASM - General Kernel Subroutines
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved

;********************************************************************
;*    CHANGE ACTIVITY:
;* @DB70340 DJB 9/8/93 Ring0 traps are not always displayed. Do not unmask
;*                     interrupts during real mode in ByteToScrnDM and restore
;*                     XGA cards to VGA mode.
;* @001SM SEM 06/26/93 system is hung after a panic, internal error.
;*        #70965       Send non-specific-eoi to 8259, mask off interrupts
;*                     other than irq 1 (keyboard)
;*         69875       Preventing system hang during int10 writeTTY by
;*                     writing characters directly to display memory.
;*         73941       Removing temporary fix defect 69875.
;*                     Now calling RealModeNoInt which does not enable IRQ
;*                     interrupts while in real mode (defect 70340 fix).
;*
;*F78693  FJT 03/11/94 Allow to debug panic with debug kernel as defect 78486
;*
;*F78693  FJT 03/11/94 Don't allow interrupts in case of panic display ;78486
;*
;*F78693  FJT 03/11/94 Changes made for AUTOIPL DCR                    ;78486
;*                     Change panic loop                               ;78486
;*
;*090770  ARJ 07/28/94 Removed defects 70965 and 69875 from this       ;90770
;*                     module they cause a sysem hang                  ;90770
;*149725  TPL 04/08/96 Disable system trace on entry to _tkPanic
;*********************** END OF SPECIFICATIONS **********************

.xlist
.xcref
include struc.inc                                                     ;@DB70340
include dosseg.inc
include basemaca.inc
include fnotask.inc
include osmaca.inc
include mi.inc
include dossymc.inc
include pmode.inc
include dbcs.inc
include vector.inc
include doshlp.inc
include basemid.inc
include int.inc              ;for 8259 ports                            ;@001SM
include iodelay.inc                                                    ;F78693
include systrace.inc                                                    ;149725

ifdef PMDEBUG
include infoseg.inc
endif
.cref
.list

        ModName  , FNO_SUBR_ASM
        CpuMode 386

        ReferGlobal     DUMP_ON,BYTE                                   ;F78693
        ReferGlobal     REIPL_ON,BYTE                                  ;F78693
        ReferGlobal     UserNum,WORD
        ReferGlobal     MSVERS,WORD
        ReferGlobal     InDos,BYTE
        ReferGlobal     Header,byte
        ReferGlobal     DHF,DWORD
        ReferGlobal     _CpuStepping,DWORD
        ReferGlobal     _SISData,byte
        ReferGlobal     _InitCodeInvalid
        ReferGlobal     ras_systr_flags,BYTE                            ;149725
        EXTRNFAR        syiResetVideoMode

        ReferFlat       ABIOS_present,BYTE                            ;@DB70340

        ReferGDT        GDT_MSSTACK,WORD

        ReferTask       CP_CaseMapTbl,DWORD

        EXTRNF32        ldrGetObjOff
        EXTRNF32        ldrVersionLie
        EXTRNF32        _TKSetSSBase
        EXTRNF32        INTDHEOI                                       ;F78693

        EXTRN32         _SELVirtToLin

        extrn           MSSTACKSIZE:ABS
        extrn           HeaderLen:abs

        extrn           FindChar:near
        extrn           RealModeNoInt:near                            ;@DB70340
        extrn           ProtMode:near

IF        RAS                                                          ;83839
        EXTRN           RASRST:FAR      ; Performs system dump         ;F78693
ENDIF  ;  RAS                                                          ;83839

ifdef PMDEBUG
        extrn           dputcharnl:near
        extrn           Debug_BreakDM:near
endif


MASTER_IMR      equ     21h             ; mask port for master 8259


DATA segment
EVEN
fResetVideoMode db      0               ; if 0, reset video mode

; Moved to sdata.asm for System Anchor Block - f122427
ifdef PMDEBUG
        extrn   fDebugOnly:byte
endif

        extrn  _tkPanicActive:byte
        extrn   nPanic:byte                                            ;F78693
        extrn   fPanicToScreen:dword                                   ;F78693

        EVEN

address_item    db      '  '            ; protect/real mode/paging indicators
address_string  db      'xxxx:xxxx    ' ; address detecting InternalError
LEN_ADDRESS_ITEM equ    $-address_item  ;

object_item     db      ' - '
object_string   db      'xxxx:xxxx    ' ; obj#:offset detecting InternalError
LEN_OBJECT_ITEM equ     $-object_item   ;




ifdef INTERRPRT
line_string     db      "line "
LEN_ERROR_STRING equ    $-line_string
endif
;               db      "%d"            ; line number
file_string     db      ", "
ifdef INTERRPRT
                db      "in file "
endif
;               db      "%d"            ; file number
LEN_FILE_STRING equ     $-file_string
;               db      " %s"           ; file name & SCCSID
;               db      CR, LF
;               db      " %s"           ; message text




stepping_string db      'xxxxxxxx'      ; cpu stepping
ifdef PMDEBUG
                db      ' - '           ; to separate date/time
endif
LEN_STEPPING_STRING equ $-stepping_string

ifdef PMDEBUG
time_item       db      'dd:'           ; day, hour, minutes or seconds
LEN_TIME_ITEM equ       $-time_item
time_offsets    db      offset SIS_MonDate      ; Month (1-12)
                db      '/'
                db      offset SIS_DayDate      ; Day-of-month (1-31)
                db      ' '
                db      offset SIS_HrsTime      ; Hours
                db      ':'
                db      offset SIS_MinTime      ; Minutes
                db      ':'
                db      offset SIS_SecTime      ; Seconds
                db      '.'
                db      offset SIS_HunTime      ; Hundredths of seconds
                db      0                       ; terminates list
endif ; PMDEBUG


new_line        db      c_CR,c_LF       ; CR/LF
LEN_NEW_LINE    equ     $-new_line

decimal_string  db      11 dup(?)

blank_string    db      " "
LEN_BLANK       equ     $-blank_string

firstcall2byte2scrn db 0                 ; Do we need to kill logo?

DATA ends


        FARCODE LOW
CODE segment

;***    w_Get_Version get DOS version number
;
;       PaulDev : Modified to support kernel lie list in 2.1 (Borg).
;       If LieList bit is on in the MTE of the owning PTDA, or if
;       this is a LieList PDD/IFS, then return version=2.0, else
;       return version=2.1.
;
;       ENTRY   NONE
;
;       EXIT    AH = Major version number
;               AL = Minor version number
;               BH = OEM serial number
;               BL:CX = 24 bit user (serial) number
;               NOTE: On pre 1.28 DOSs AL will be zero
;
;       USES    AX, BX, CX, DS
;

Procedure w_Get_Version,<near,faronly,far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA

        DosContext ds

        CALLF32 ldrVersionLie         ; ldr will fill in version info if this
                                      ; is a lie list module. Returns nC if
                                      ; filled in, C if not in lie list.

        mov     bx,[UserNum + 2]
        mov     cx,[UserNum]

        jnc     short wGVx

        mov     ax,[MSVERS]
        clc

wGVx:   RETC

EndProc w_Get_Version


;***    DosHlpErr - Complains the discarded doshlp function gets called
;
;       Called via far call.
;       After SysInit, the discarded doshlp function entries in DosHlp
;       function table are redirected to this routine.
;
Procedure DosHlpErr,faronly
    assume ds:nothing,es:nothing,ss:nothing
        InternalError <Discarded DosHlp called>
EndProc DosHlpErr


;***    _InternalError - Internal Error for FSH/DevHelp Interface
;
;       ENTRY   (DS:SI) -> message_text of '$' or nul byte terminated string
;               (CX)    = message text length
;       EXIT    Doesn't.
;       USES    Doesn't matter.

Procedure _InternalError,faronly
    assume ds:nothing,es:nothing,ss:nothing
        push    0                       ; push offset.hiword of message
        push    si                      ; push offset.loword of message
        PUSHD   0                       ; push NULL segment of filename
        push    eax                     ; push garbage offset of filename
        PUSHD   <(-1 shl 16) or FNO_SUBR_ASM>
        PUSHD   [esp+16]                ; re-push CS:IP
        PUSHD   [esp]                   ; re-push CS:IP
        mov     [esp].Offst32.hiword,0  ; zero EIP.hiword
        push    [esp].Pad32             ; mov CS into position
        pop     [esp].Segmt32
        mov     [esp+20].Segmt32,ds     ; set segment of message
        call    _tkPanicMsgLen          ; no deposit, no return

EndProc _InternalError


;***    _tkPanic - Internal Error
;
;       _tkPanic is jumped to after an internal consistancy failure is
;       noted.  This indicates a sick kernel, a "panic" is the only remedy.
;
;       Panic <Message>
;
;       [ESP+22] = 16:32 pointer to message (or NULL seg)  (fword)
;       [ESP+14] = 16:32 pointer to filename (or NULL seg) (fword)
;       [ESP+12] = source line number                      (word)
;       [ESP+10] = source file number                      (word)
;       [ESP+2]  = caller's CS:EIP                         (fword)
;
;       All registers and flags are preserved for subsequent examination
;       in the debugger.

Procedure _tkPanic
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
    ArgVar pnc_cseip,fword
    ArgVar pnc_fileno,word
    ArgVar pnc_lineno,word
    ArgVar pnc_pfilename,fword
    ArgVar pnc_pmsg,fword

    LocalVar pnc_msglen,word

        EnterProc <0>                   ; pnc_msglen is 0 by default
        jmp     short pnc00

Entry _tkPanicMsgLen,,local
        EnterProc <cx>, NoCheck         ; pnc_msglen

pnc00:  pushf
        pushad
        push    ds
        push    es
        DosContext ds
        or      ras_systr_flags, RF_TRCPAUSED                           ;149725
        mov     word ptr [address_item],"  " ; reset from previous call
        inc     [nPanic]                ; Count number of panics       ;F78693
        inc     [_tkPanicActive]        ; already been here?
        jnz     short pnc60             ; yes, don't try another print
        mov     ax,MSG_INTERNAL_ERROR
        push    ds
        whatmode bx
        jc      short pnc30
        call    [DHF].DosHlpRealGetMessage; (ds:si) = msg length + text
        jmp     short pnc50

pnc30:  mov     [address_item+1],"#"    ; indicate protect mode
        mov     ebx,cr0
        test    ebx,CR0_PG
        jz      short pnc40
        mov     [address_item],"#"      ; indicate paging enabled
pnc40:  call    [DHF].DosHlpProtGetMessage; (ds:si) = msg length + text

pnc50:  add     si,2                    ; ignore the length
        mov     dx,si                   ; (ds:dx) = message text
        call    ErrPrnt
        pop     ds                      ; DS is DosContext again
        dec     [_tkPanicActive]        ; restore count
        mov     [fResetVideoMode],0     ; clear the reset video mode flag
pnc60:

ifdef PMDEBUG
        cmp     [fPanicToScreen],0          ; Do panics go to debugger?;F78693
        jnz     short pnc601                ; No                       ;F78693
        mov     eax,[pnc_cseip].Offst32
        mov     [bp+?aframe-4].Offst32,eax
        movzx   eax,[pnc_cseip].Segmt32
        mov     dword ptr [bp+?aframe-4].Segmt32,eax
pnc601:                                                                ;F78693
endif ; PMDEBUG

        pop     es
        pop     ds
        popad
        popf
        LeaveProc

ifdef PMDEBUG
        push    ds                      ; Make variable is accessable  ;F78693
        DosContext ds                                                  ;F78693
        cmp     [fPanicToScreen],0      ; Do Panics go to debugger?    ;F78693
        jnz     short pnc602            ; No                           ;F78693
        pop     ds                                                     ;F78693
        lea     esp,[esp+?aframe-6]     ; adjust esp - don't touch flags
        jmp     Debug_BreakDM           ; break into the debugger
pnc602: pop     ds                                                     ;F78693
endif ; PMDEBUG                                                        ;F78693
IF        RAS                                                          ;83839
        ;--------------------------------------------------------------;F78693
        ; Following code replaced former panic loop(pnc90)             ;F78693
        ; Changed to do automatic system reboot if required            ;F78693
        ;--------------------------------------------------------------;F78693
        push    ds                      ; save DS for debugging        ;F78693
        DosContext ds                   ; Make variables addessable    ;F78693
        cmp     DUMP_ON,0               ; Dump wanted?                 ;F78693
        je      short pnc901            ; Jump if No                   ;F78693
        pop     ds                      ; Restore ds                   ;F78693
        call    RASRST                  ; Dump it (no return)          ;F78693
pnc901: cmp     REIPL_ON,0              ; REBOOT ?                     ;F78693
        je      short pnc902            ; Jump if No                   ;F78693
        cli                             ; Disable interrupts           ;F78693
        DosContext ds                   ; Ensure [DHF] is addressable  ;F78693
        call    [DHF].DosHlpReboot      ; never returns                ;F78693
        jmp     short pnc903            ; Better save than sorry       ;F78693
pnc902: pop     ds                      ; Restore ds                   ;F78693
        ;--------------------------------------------------------------;F78693
        ; Unmask interupts needed by the keyboard                      ;F78693
        ;--------------------------------------------------------------;F78693
pnc903: push    ds                      ; save DS for debugging        ;F78693
        push    eax                     ; Save register                ;F78693
        DosContext ds                   ; Make variables addessable    ;F78693
        mov     eax,0000fef8h           ; Enable keyboard&timer&rtimer ;F78693
        call    [DHF].DosHlpSetMask     ; Set interrupt mask           ;F78693
        ;--------------------------------------------------------------;F78693
        ; Since there may be a an interrupt pending on each IRQ level  ;F78693
        ; Present EOI to each to make sure the keyboard is enabled     ;F78693
        ;--------------------------------------------------------------;F78693
        mov     ax,NUM_IRQ              ; Shift by number of IRQ levels;F78693
pnc907: dec     ax                      ; Start at highest IRQ level   ;F78693
        CCALL32 INTDHEOI                ; Send none specific EOI       ;F78693
        cmp     ax,0                    ; Done ?                       ;F78693
        jne     short pnc907            ; No                           ;F78693
        pop     eax                     ; Restore register             ;F78693
        pop     ds                      ; Restore ds                   ;F78693
        ;--------------------------------------------------------------;F78693
        ; Flush keyboard buffer to make shure ALT-CRTL-... works       ;F78693
        ; Because mouse interrupts are disabled and its data is coming ;F78693
        ; through the same register as the keyboard data disable the   ;F78693
        ; auxiliary device(mouse). Mouse data not beeing processed     ;F78693
        ; would block keyboard data. ALT_CTRL-.. would not work.       ;F78693
        ;--------------------------------------------------------------;F78693
        push    eax                     ; Save register                ;F78693
        push    ebx                     ; Save register                ;F78693
        push    cx                      ; Save register                ;F78693
        mov     al,0a7h                 ; Disable auxillary device     ;F78693
        out     64h,al                  ; Write it to cmd port         ;F78693
        kIODelay bx                                                    ;F78693
pnc908: in      al,64h                  ; Read keyboard status reg.    ;F78693
        kIODelay bx                                                    ;F78693
        test    al,01                   ; Input buffer full(bit set)   ;F78693
        jz      short pnc909            ; No                           ;F78693
        in      al,60h                  ; Read in buffer               ;F78693
        kIODelay bx                                                    ;F78693
        jmp     short pnc908            ; Get next character           ;F78693
pnc909: pop     cx                      ; Restore register             ;F78693
        pop     ebx                     ; Restore register             ;F78693
        pop     eax                     ; Restore register             ;F78693
        ;----------------------------------------------------------------------
ENDIF  ;  RAS                                                          ;83839
pnc904: sti                             ; enable ints.(ctrl-alt-del)   ;F78693
        jmp     short pnc904            ; sit here forever             ;F78693
EndProc _tkPanic

;***    ErrPrnt - print a system error message
;
;       ENTRY   (DS:DX) = start of error message
;               (BP)    = _tkPanic BP frame
;       EXIT    Error message is printed
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, DS, ES, Flags

Procedure ErrPrnt,,local
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        SaveReg <ds,dx>
        DosContext <ds,es>,ax           ; Set ES to DOSGROUP
        call    DisplayCrLf             ; display CR/LF to start
        RestoreReg <dx,ds>

        mov     si,dx                   ; start at beginning of text
        mov     cx,-1                   ; initialize loop counter

;       (CX) = negated undisplayed text length
;       (DX) = pointer to beginning of undisplayed text
;       (SI) = pointer to current character in text

erp10:  lodsb                           ; pick up next byte of text
        or      al,al                   ; null byte - end of text
        je      short erp50
        cmp     al,'%'                  ; is this a fill-in item?
        jne     short erp40

;       Process a fill-in item

        not     cx                      ; display the section of text
        SaveReg <si>                    ; which preceeds the
        movzx   edx,dx
        call    WrtToScrn               ; fill-in item
        RestoreReg <si>                 ;

        lodsb                           ; pick up fill-in number
        SaveReg <ds,si>                 ; remember where we are
        DosContext <ds>                 ; (DS) = DosGroup
        cmp     al,'1'                  ; fill-in item 1 is address
        jne     short erp20
        call    DisplayAddress          ; Display address of the error
        jmp     short erp30

erp20:  cmp     al,'2'                  ; fill-in item 2 is message
        jne     short erp30
        call    DisplayFmt              ; Display formated message (if any)
        call    DisplayAux              ; Display extra stuff (stepping, date)

erp30:  RestoreReg <si,ds>              ; continue where we left off
        assume  ds:nothing
        xor     cx,cx                   ; reinitialize loop counter
        mov     dx,si                   ; point to next text section

erp40:  loop    erp10                   ; loop through the message text

;       Display the last section of the message

erp50:  not     cx                      ; display the section of text
        movzx   edx,dx
        call    WrtToScrn               ; following the last fill-in item
        RETC                            ; return to caller
EndProc ErrPrnt


;***    DisplayAddress - display address where InternalError was detected
;
;       Displays InternalError detection address, and if in protected mode,
;       attempts to convert and display the object number and offset (for
;       kernel addresses only).
;
;       ENTRY   (BP) = _tkPanic BP frame
;               (DS) = DOSGROUP
;               (ES) = DOSGROUP
;       EXIT    NONE
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags

Procedure DisplayAddress,,local
    assume ds:DOSGROUP,es:DOSGROUP,fs:nothing,gs:nothing,ss:nothing
        mov     ax,[pnc_cseip].Segmt32  ; (ax:ebx) = selector offset
        mov     ebx,[pnc_cseip].Offst32
        mov     cx,LEN_ADDRESS_ITEM
        mov     di,offset address_string; point to xxxx:xxxx area
        call    FwordToAscii

        mov     di,offset address_item  ; display the address string
dad20:  cmp     byte ptr [di],' '       ; but skip leading blanks
        jnz     short dad40
        inc     di
        dec     cx
        jmp     short dad20

dad40:  movzx   edx,di
        call    WrtToScrn

        cmp     byte ptr [di],"#"       ; in protect mode?
        jnz     short dadx              ; skip obj:off translation in real mode

        push    ds
        push    es
        FlatContext <ds,es>
        mov     ax,[pnc_cseip].Segmt32  ; (ax:ebx) = selector offset
        mov     ebx,[pnc_cseip].Offst32
        CALLF32 ldrGetObjOff            ; (ax:ebx)=obj:off=ldrGetObjOff(ax:ebx)
        pop     es
        pop     ds
    assume ds:DOSGROUP, es:DOSGROUP
        jc      short dadx              ; jmp if not a valid kernel address

        mov     cx,LEN_OBJECT_ITEM
        mov     di,offset object_string ; point to xxxx:xxxx area
        call    FwordToAscii

        mov     edx,offset object_item  ; display the address string
        call    WrtToScrn
dadx:   RETC
EndProc DisplayAddress


;***    DisplayFmt - display InternalError explanatory message
;
;       Displays Panic formatted message, with source line and file numbers,
;       and possibly the filename and a message.
;
;       ENTRY   (BP) = _tkPanic BP frame
;               (DS) = DOSGROUP
;               (ES) = DOSGROUP
;       EXIT    NONE
;       USES    ECX, EDX, ESI, EDI, Flags

Procedure DisplayFmt,,local
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
ifdef INTERRPRT
        mov     cx,LEN_ERROR_STRING
        mov     edx,offset line_string
        call    WrtToScrn
endif
        mov     ax,[pnc_lineno]
        call    DisplayDecimal
        mov     cx,LEN_FILE_STRING
        mov     edx,offset file_string
        call    WrtToScrn
        mov     ax,[pnc_fileno]
        call    DisplayDecimal
        mov     cx,LEN_BLANK
        mov     edx,offset blank_string
        call    WrtToScrn
        lea     bx,[pnc_pfilename]
        xor     cx,cx                   ; no known length
        call    DisplayMsg
        call    DisplayCrLf             ; display CR/LF
        lea     bx,[pnc_pmsg]
        mov     cx,[pnc_msglen]         ; (cx) = length (if any)
        call    DisplayMsg
        RETC
EndProc DisplayFmt


;***    DisplayDecimal - display an integer in decimal ascii
;
;       Displays integer after converting to decimal ascii
;
;       ENTRY   (AX) = 16 bit integer to display
;               (DS) = DOSGROUP
;               (ES) = DOSGROUP
;       EXIT    NONE
;       USES    ECX, EDX, ESI, EDI, Flags

Procedure DisplayDecimal,,local
    assume ds:DOSGROUP,es:DOSGROUP,fs:nothing,gs:nothing,ss:nothing
        movzx   eax,ax
        mov     edi,offset decimal_string
        push    edi
        call    IntToDecAscii
        pop     edx
        mov     cx,di
        sub     cx,dx
        call    WrtToScrn
        RETC
EndProc DisplayDecimal


;***    DisplayMsg - display a message string
;
;       Displays message pointed to by 16:32 far pointer
;
;       ENTRY   (SS:BX) = address of 16:32 far pointer to string
;               (CX)    = length or 0
;               (DS)    = DOSGROUP
;               (ES)    = DOSGROUP
;       EXIT    NONE
;       USES    ECX, EDX, ESI, EDI, Flags

Procedure DisplayMsg,,local
    assume ds:DOSGROUP,es:DOSGROUP,fs:nothing,gs:nothing,ss:nothing
        cmp     ss:[bx].Segmt32,0
        jz      short dmsx

        push    ds
        push    es
        or      cx,cx
        jnz     short dms40             ; jmp if length already given
        les     edi,ss:[bx]
        call    StrLen32
        dec     cx

dms40:  lds     edx,ss:[bx]
        call    WrtToScrn

        pop     es
        pop     ds

dmsx:   RETC
EndProc DisplayMsg


;***    DisplayAux - display stepping, date info
;
;       Displays InternalError auxiliary messages: Cpu Stepping followed
;       by the Header message (build number or julian build date).
;
;       ENTRY   (BP) = _tkPanic BP frame
;               (DS) = DOSGROUP
;               (ES) = DOSGROUP
;       EXIT    NONE
;       USES    ECX, EDX, ESI, EDI, Flags

Procedure DisplayAux,,local
    assume ds:DOSGROUP,es:DOSGROUP,fs:nothing,gs:nothing,ss:nothing

;       display the Cpu Stepping.

        call    DisplayCrLf
        mov     di,offset stepping_string
        movzx   edx,di
        mov     ax,[_CpuStepping].hiword
        call    HexToAscii              ; convert high word
        mov     ax,[_CpuStepping].loword
        call    HexToAscii              ; convert low word
        mov     cx,LEN_STEPPING_STRING
        call    WrtToScrn               ; display the stepping

;       display the date and time.

ifdef PMDEBUG
        mov     si,offset time_offsets
dms80:  lodsw                           ; (AL)=infoseg offset, (AH)=separator
        mov     ch,ah                   ; save separator
        mov     di,offset time_item
        movzx   edx,di
        xor     ah,ah                   ; (AH) = 0
        mov     bx,ax                   ; (BX) = infoseg offset
        mov     al,[_SISData+bx]        ; pick up byte infoseg field
        mov     cl,10                   ; (AL) = (AX) / CL
        div     cl                      ; (AL) = quotient, (AH) = remainder
        add     ax,"00"                 ; convert to ascii
        stosw
        mov     al,ch
        stosb                           ; store separator
        mov     cx,LEN_TIME_ITEM
        or      al,al
        jnz     short dms90
        dec     cx
dms90:  push    si
        push    ax
        call    WrtToScrn
        pop     ax
        pop     si
        or      al,al
        jnz     short dms80
endif ; PMDEBUG

;       display the build number or julian build date.

        mov     edx,offset Header       ; point to version message
        mov     cx,HeaderLen            ; indicate length
        call    WrtToScrn               ; display the message
        RETC
EndProc DisplayAux


;***    DisplayCrLf - display CR and LF
;
;       ENTRY   (DS) = DOSGROUP
;               (ES) = DOSGROUP
;       EXIT    NONE
;       USES    AX, CX, EDX, SI, Flags

Procedure DisplayCrLf, hybrid
    assume ds:DOSGROUP,es:DOSGROUP,fs:nothing,gs:nothing,ss:nothing
        mov     edx,offset new_line
        mov     cx,LEN_NEW_LINE
        call    WrtToScrn
        RETC
EndProc DisplayCrLf


;***    FwordToAscii - convert a 16:32 pointer to an ascii hex string
;
;       ENTRY   (AX)    = selector/object number
;               (EBX)   = offset
;               (CX)    = string len
;               (ES:DI) = address string to be filled in ("xxxx:xxxx    ")
;       EXIT    (CX)  = string len reduced by 4 if offset < 64k
;       USES    AX, CX, DI, Flags

Procedure FwordToAscii,,local
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        call    HexToAscii              ; convert selector/object number
        inc     di                      ; skip colon
        mov     eax,ebx
        shr     eax,16
        sub     cx,4                    ; assume 16 bit offset
        or      ax,ax
        jz      short fta20
        add     cx,4                    ; oops is 32 bit offset
        call    HexToAscii              ; convert high word of offset
fta20:  mov     ax,bx
        call    HexToAscii              ; convert low word of offset
        RETC
EndProc FwordToAscii


;***EP  IntToDecAscii - converts a long integer to an ASCII decimal number
;
;       The ascii decimal number has leading zeros removed, and may contain
;       as many as 11 bytes, including the trailing nul byte.  An 11 byte
;       buffer is required!
;
;       May be called with DS,ES FLAT or non-FLAT.
;
;       ENTRY   (EAX) = the number to be converted
;               (EDI) = DS,ES-relative destination buffer pointer
;       EXIT    (EDI) = pointer to nul terminating byte
;       USES    EAX, ECX, EDX, ESI, EDI, Flags

Procedure IntToDecAscii,<near,faronly,far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        mov     esi,edi                 ; (ESI) = base of result
        add     edi,10
        std                             ; build initial string backwards
        push    ax
        xor     al,al
B1EMac  stos    byte ptr es:[edi]       ; nul-terminate initial string
        pop     ax
        mov     ecx,10                  ; (ECX) = radix (base 10)

;       (EAX) = integer/quotient to be converted
;       (ECX) = 10 (radix)
;       (ESI) = base of buffer
;       (EDI) = base of string being built backwards

wta10:  xor     edx,edx                 ; (EDX),(EAX) = (0:EAX) / (ECX)
        div     ecx                     ; (EAX) = quotient, (EDX) = remainder
        xchg    al,dl                   ; (AL) = remainder
        add     al,'0'                  ; (AL) = ascii digit
B1EMac  stos    byte ptr es:[edi]       ; drop in the character
        xchg    al,dl                   ; (EAX) = quotient from last division
        or      eax,eax                 ; stop when (EAX) == 0
        jnz     short wta10

        cld                             ; copy string from beginning
        xor     cx,cx                   ; indicate drop leading '0's
        inc     edi                     ; point to first byte stored
        xchg    esi,edi                 ; (ESI) = initial, (EDI) = final

wta60:  lods    byte ptr ds:[esi]       ; grab a byte
        cmp     al,'0'
        jnz     short wta70             ; copy if not '0'
        cmp     byte ptr [esi],0
        jz      short wta70             ; or at end of initial string
        jcxz    short wta60             ; or not leading
wta70:  inc     cx
B1EMac  stos    byte ptr es:[edi]       ; drop in the character
        or      al,al
        jnz     short wta60             ; stop after trailing nul

        dec     edi
        RETC
EndProc IntToDecAscii


;***    WrtToScrnClr - write string of bytes to console
;
;       This function resets the video mode to text if WrtToScrn hasn't been
;       called before.  Writes the string out the console.  Clears the reset
;       video mode flag so the next WrtToScrn or WrtToScrnClr resets the video
;       mode to text happens again.
;
;       The difference between WrtToScrnClr and WrtToScrn is the WrtToScrn will
;       only reset the video the first time it is called and WrtToScrnClr
;       resets the video mode every time it is called.
;
;       WrtToScrnClr should be used for messages that are not fatal and the
;       screen mode has a chance to be changed back to graphics.
;
;       ENTRY   (DS:EDX) = address of string of bytes
;               (CX)     = length of string of bytes
;       EXIT    NONE
;       USES    AX, SI, Flags
;
;       NOTE    this function should be called in kernel mode only

Procedure WrtToScrnClr,faronly
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        call    WrtToScrn
        push    ds
        DosContext ds
        mov     [fResetVideoMode],0     ; clear the reset video mode flag
        pop     ds
        RETC
EndProc WrtToScrnClr


;***EP  WrtToScrn16 - same as WrtToScrn, except accepts 16 bit offset
;
;       ENTRY   (DS:DX) = address of string of bytes
;               (CX)     = length of string of bytes
;       EXIT    NONE
;       USES    AX, EDX, ESI, Flags

Procedure WrtToScrn16,faronly
        movzx   edx,dx
        call    WrtToScrn
        ret
EndProc WrtToScrn16


;***EP  WrtToScrn - write string of bytes to console
;
;       This function resets the video mode to text if WrtToScrn hasn't been
;       called before.  Writes the string out the console.
;
;       This function is used when multiple panic/fatal messages are printed
;       out and the video mode change/screen clear is not wanted between each
;       message.
;
;       ENTRY   (DS:EDX) = address of string of bytes
;               (CX)     = length of string of bytes
;       EXIT    NONE
;       USES    AX, ESI, Flags
;
;       NOTE    this function should be called in kernel mode only

Procedure WrtToScrn,<far16,faronly,near>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        jcxz    short wrs1
        push    cx
        mov     esi,edx
        cld
wrs0:   lods    byte ptr ds:[esi]
        call    ByteToScrnDM
        loop    wrs0
        pop     cx
wrs1:   RETC
EndProc WrtToScrn

; @DB70340 - Added function
;
;***    test_xga - test for XGA card & reset if present
;
;       ENTRY - NONE
;       EXIT  - XGA card set to VGA mode if present.
;       USES  - NONE
;
Procedure test_xga,near,local
    assume ds:FLAT,es:nothing,fs:nothing,gs:nothing,ss:nothing

        PLANAR  EQU     7
        MAXSLOT EQU     0Fh
        XGA_LOW EQU     8fd8h
        XGA_HI  EQU     8fdbh
        IDX_SEL EQU     0ah
        IDX_DAT EQU     0bh
        MON_ID  EQU     52h

        EnterProc
        pusha

        FlatContext ds,ax               ; to access ABIOS_present
        sub     di,di                   ; Assume XGA not present
        cli                             ; Disable interrupt

        mov     cx,PLANAR               ; Start with PLANAR
        .repeat
           .if <cx e PLANAR>
              in      al, 94h           ; Read planar POS
              mov     bh, al            ; Save planar setting
              mov     al, 0DFh          ;
              out    94h, al            ; Put planar into setup mode
           .else
              in      al, 96h           ; Read card slot setting
              mov     bh, al            ; Save card slot setting
              mov     al, cl            ;   XGA with a display attached
              out    96h, al            ; Put card slot into setup mode
           .endif

           mov      dx, 100h            ;
           in       ax, dx              ; Get POS ID
           xchg     ax, bx
           add      dx, 2               ; and byte 1
           in       al, dx
           xchg     ax, bx              ; AX <= POS ID, BL <= Byte 1

           xchg     al,bh               ; save AX
           .if <cx e PLANAR>
              out    94h, al            ; Restore planar setting
           .else
              out    96h, al            ; Restore card slot setting
           .endif
           xchg     al,bh               ; restore ax

           .if <ABIOS_present e 0> and
           .if <cx ne PLANAR>           ; Type 1 machine check for radius XGA
              .if <ax lt XGA_LOW> or
              .if <ax gt XGA_HI>
                 mov    dx, 100h
                 add    dx, cx          ; go to radius pos regs 108-10f
                 in     al, dx          ; Get original value
                 mov    bh, al          ; save it
                 mov    al, cl          ; start again, this time for......
                 out    dx, al          ; Put card into setup mode
                 jmp    $+2
                 mov    dx, 100h        ; restore dx
                 in     ax, dx          ; Get POS ID from slot
                 xchg   ax, bx
                 add    dx, 2           ; get byte1
                 in     al, dx
                 xchg   ah, al          ; get value to restore
                 mov    dx, 100h
                 add    dx, cx          ; get POS register to restore
                 out    dx, al          ; restore POS value
                 xchg   al, ah          ; restore AX & BL
                 xchg   ax, bx
              .endif
           .endif

           .if <ax ge XGA_LOW> and      ; XGA Monitor found
           .if <ax le XGA_HI>
              and     bx, 0eh
              shl     bx, 3
              add     bx, 2100h         ; BX <= IoRegBase

              mov     dx,bx
              add     dx,IDX_SEL        ; monitor attached?
              mov     al,MON_ID
              out     dx,al

              mov     dx,bx
              add     dx,IDX_DAT
              in      al,dx
              and     al,0fh
              .if <al ne 0fh>
                 mov     di,1           ; Flag XGA present
              .endif
           .endif

           inc     cx
        .until <cx gt MAXSLOT> NEAR or
        .until <di ne 0> NEAR

        .if <di ne 0>
           mov     dx,bx                ; Is it in VGA mode?
           in      al,dx
           test    al,1
           .if <z>
              mov     al,0              ; no -
              inc     dx                ; Setup VGA mode
              out     dx,al
              jmp     $+2

              add     dx,3              ; Values as per XGA Users Guide
              out     dx,al
              jmp     $+2

              not     al
              inc     dx
              out     dx,al
              jmp     $+2

              add     dx,5
              mov     ax,0ff64h
              out     dx,ax
              jmp     $+2

              mov     ax,01550h
              out     dx,ax
              jmp     $+2

              mov     ax,01450h
              out     dx,ax
              jmp     $+2

              mov     ax,00051h
              out     dx,ax
              jmp     $+2

              mov     ax,00454h
              out     dx,ax
              jmp     $+2

              mov     ax,07f70h
              out     dx,ax
              jmp     $+2

              mov     ax,0202ah
              out     dx,ax
              jmp     $+2

              mov     dx,bx
              mov     al,1
              out     dx,al
              jmp     $+2

              mov     dx,3c3h
              mov     al,1
              out     dx,al
           .endif
        .endif

        sti
        popa
        ExitProc

EndProc test_xga
; @DB70340 - End of Insertion

;***EP  ClearAllInts - Mask all interrupts                             ;F78693
;                                                                      ;F78693
;       This function masks all interrupts on the 8259 controller      ;F78693
;       and sends a none specific EOI to master and slave PIC.         ;F78693
;                                                                      ;F78693
;       This function is called by systemFatalFault                    ;F78693
;                                                                      ;F78693
;       ENTRY   none                                                   ;F78693
;       EXIT    All interrupts are masked, and disabled                ;F78693
;       USES    AX, DS, Flags                                          ;F78693
;                                                                      ;F78693
;       NOTE    this function should be called in kernel mode only     ;F78693
                                                                       ;F78693
Procedure ClearAllInts,<far16,faronly,near>                            ;F78693
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing      ;F78693
        push    eax                      ; Save register               ;F78693
        push    ds                       ; Save register               ;F78693
        cli                              ; Disable interrupts          ;F78693
        DosContext ds                    ; Make DHF accessable         ;F78693
        mov     eax,0ffffh               ; all OFF                     ;F78693
        call    DHF.DosHlpSetMask        ; Mask interrupts             ;F78693
        mov     ax,NUM_IRQ               ; EOI to master and slave     ;F78693
        shr     ax,1                     ; Highest IRQ level           ;F78693
        CCALL32 INTDHEOI                 ; Send none specific EOI      ;F78693
        pop     ds                       ; Restore register            ;F78693
        pop     eax                      ; Restore register            ;F78693
        RETC                             ; Return to caller            ;F78693
EndProc ClearAllInts                                                   ;F78693

;***    ByteToScrnDM - write byte to console
;
;       Note: this function uses ROM INT 10's to do its
;       output.  When the function is called in protected
;       mode, we must not only switch to real mode, but
;       we must first switch to a stack that is known to
;       be in low memory.
;
;       ENTRY   (AL) = byte to write
;       EXIT    NONE
;       USES    AL, Flags

Procedure ByteToScrnDM,<near,faronly,far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
;       DUALMODE
        cmp     al,9                    ; translate tab in SCCSIDs to space
        jnz     short bts0
        mov     al,' '
bts0:
ifdef PMDEBUG
        call    dputcharnl              ; send the char to debugger serial port
endif

        push    ds

ifdef PMDEBUG
        DosContext ds
        test    [fDebugOnly],-1         ; debugger only?
        jnz     bts90                   ; don't send to screen
endif

        DosContext ds
        .if <firstcall2byte2scrn eq 0>        ; If this is the first call here
            inc     firstcall2byte2scrn
            .if <_InitCodeInvalid eq 0>       ; And initcode is valid,
                 pushad
                 CALLFAR syiResetVideoMode    ; and Reset video
                 popad
            .endif
        .endif

        push    es
        pushad                          ; save registers

        ; @DB70340 - we need to check for an XGA display.
        DosContext ds
        cmp     [fResetVideoMode],0
        jne     short bts_x1
        call    test_xga                ; test for XGA
bts_x1:
        ; @DB70340 - End of insertion

        pushf                           ; Save current status reg.     ;F78693
        cli                             ; No interupts allowed         ;F78693

;       Mask off master 8259 to prevent all interrupts

        xchg    ax,bx                   ; (BL) = character to output

        DosContext ds
        call    DHF.DosHlpGetMask
        push    eax
;73941        mov     eax,0ffh                ; all OFF
        mov     eax,-1h                 ; all OFF                         73941

        call    DHF.DosHlpSetMask

        xchg    ax,bx                   ; (AL) = character to output

;       Check protected or real mode

        smsw    bx                      ; get MSW
        test    bl,1                    ; protected mode?
        jz      short bts1              ;  no, skip ahead

        mov     cx,ss                   ; save current SS:SP in CX:DX
        mov     dx,sp
        push    GSEL GDT_MSSTACK        ; switch to mode switch stack
        pop     ss
        mov     sp,MSSTACKSIZE          ; SS:SP -> top of mode switch stack

        push    eax
        push    ecx
        push    edx
        push    ds
        FlatContext ds,ax,NOASSUME
        CALLF32 _TKSetSSBase,,nocheck   ; Must do after every stack switch
        pop     ds
        pop     edx
        pop     ecx
        pop     eax

        push    cx                      ; save stack link
        push    dx
        call    RealModeNoInt           ; go to real mode             ;@DB70340
        ;--------------------------------------------------------------;F78693
        ; Since RealMode unmasked some interrupts; Mask them again     ;F78693
        ;--------------------------------------------------------------;F78693
bts1:   mov     cx,ax                   ; save character               ;F78693
        DosContext ds                                                  ;F78693
        call    DHF.DosHlpGetMask       ; Get current mask             ;F78693
        push    eax                     ; Save for later restore       ;F78693
                                                                       ;F78693
        mov     eax,0ffffh              ; all OFF                      ;F78693
        call    DHF.DosHlpSetMask       ; Mask them all                ;F78693
        mov     ax,cx                   ; Get back caharcter           ;F78693
        ;--------------------------------------------------------------;F78693

        push    bx                      ; save original MSW on stack

        DosContext ds
        cmp     [fResetVideoMode],0     ; reset the video if 0
        jne     short bts2

        mov     [fResetVideoMode],-1
        push    ax                      ; save character

        ; @DB70340 - if XGA has just been reset this is neccessary, otherwise
        ;            it doesn't hurt so why not!
        mov     ax,1202h
        mov     bl,30
        call    DHF.DosHlpCallInt10     ; 400 scan lines
        ; @DB70340 - End of Insertion

        mov     ax,VIDEOMODE_CO80
        call    DHF.DosHlpCallInt10     ; switch to text mode/clear screen
        pop     ax                      ; restore character

bts2:   mov     ah,INT10_WRITETTY       ; write teletype to active page
        call    DHF.DosHlpCallInt10
        pop     bx                      ; AX = original MSW            ;F78693
        ;--------------------------------------------------------------;F78693
        ;             Restore original interrup mask                   ;F78693
        ;--------------------------------------------------------------;F78693
        cli                             ; No interrupts allowed        ;F78693
        pop     eax                                                    ;F78693
        DosContext ds                                                  ;F78693
        call    DHF.DosHlpSetMask                                      ;F78693
        ;--------------------------------------------------------------;F78693
        mov     ax,bx                                                  ;F78693

        shr     ax,1                    ; protected mode?
        jnc     short bts3              ;  no, skip ahead

        call    ProtMode                ; return to protected mode
        pop     ax                      ; AX = saved SP
        pop     ss                      ; switch stacks
        mov     sp,ax

        push    eax
        push    ecx
        push    edx
        push    ds
        FlatContext ds,ax,NOASSUME
        CALLF32 _TKSetSSBase,,nocheck   ; Must do after every stack switch
        pop     ds
        pop     edx
        pop     ecx
        pop     eax

;       Enable master 8259

bts3:   pop     eax                     ; Get caller's interrupt mask  ;F78693
        DosContext ds
        call    DHF.DosHlpSetMask
        popf                            ; Get original status reg;     ;F78693
        popad
        pop     es
bts90:  pop     ds
        RETC

EndProc ByteToScrnDM


;***    HexToAscii - convert a word into an ASCII hex number.
;***    ByteToAscii - convert a byte into an ASCII hex number.
;
;       ENTRY:  AX/AL contains the number to be converted
;               ES:DI point to destination
;
;       EXIT:   ES:DI updated
;
;       USES:   AX, DI, Flags

Procedure HexToAscii,hybrid
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
;       DUALMODE
        push    ax
        mov     al,ah
        call    ByteToAscii
        pop     ax
Entry ByteToAscii,hybrid
        push    ax
        shr     al,4
        call    NibbleToAscii
        pop     ax
Entry NibbleToAscii,,local
        and     al,0fh
        add     al,90h
        daa
        adc     al,40h
        daa
        or      al,20h          ; to lower case
B1EMac  stosb                   ; store the ascii character
        RETC
EndProc HexToAscii


;***    StrLen  - Compute length of string ES:DI
;
;       ENTRY   (ES:DI) = pointer to string
;       EXIT    (CX)    = length of string INCLUDING the NUL
;       USES    CX, Flags

Procedure StrLen,<faronly, far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        push    di
        push    ax
        xor     al,al
        mov     cx,-1
        repne scas byte ptr es:[di]
        not     cx
        pop     ax
        pop     di
        RETC
EndProc StrLen


;***    StrLen32 - Compute length of string ES:EDI
;
;       ENTRY   (ES:EDI) = pointer to string
;       EXIT    (ECX)    = length of string INCLUDING the NUL
;       USES    ECX, Flags

Procedure StrLen32,<near,faronly,far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        push    edi
        push    ax
        xor     al,al
        mov     ecx,-1
        repne scas byte ptr es:[edi]
        not     ecx
        pop     ax
        pop     edi
        RETC
EndProc StrLen32


;***    DStrLen  - Compute length of string DS:SI
;
;       ENTRY   DS:SI - pointer to string
;       EXIT    CX is size of string INCLUDING the NUL
;       USES    CX

Procedure DStrLen,faronly
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        CALL    XCHGP
        CALL    StrLen
        CALL    XCHGP
        RETC
EndProc DStrLen


;***    XCHGP - exchange DS:SI with ES:DI (or DS:DI, ES:SI)
;
;       ENTRY   DS:SI source pointer
;               ES:DI destination pointer
;       EXIT    ES:DI - previous contents of DS:SI
;               DS:SI - previous contents of ES:DI
;       USES    NONE

Procedure XCHGP,hybrid
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        SaveReg <ds,es>
        RestoreReg  <ds,es>
        xchg    si,di
        RETC
EndProc XCHGP


CODE ends


        FARCODE HIGH2
HIGH2CODE segment


;***    $TJMP - Table Jump
;
;       $TJMP is an assembly-language version of the "SWITCH/CASE"
;       statement.  It compares an index against the proper bounds
;       and either jumps to the selected case or jumps to the error
;       address.
;
;       MOV     AL,index
;       CALL    $TJMP
;       DB      <maxindex>              ; largest legal index
;       DW      erroraddr               ; offset to error location
;       DW      index-0-addr            ; go here if index == 0
;       DW      index-1-addr            ; go here if index == 1
;       .            .
;       .            .
;       DW      index-N-addr            ; go here if index == N
;
;       ENTRY   (AL) = index
;
;       EXIT    through one of the vectors
;
;       USES    ax,bx,flags
;
;       WARNING  The target entries in the jump table must be in the same
;                segment as the jump table itself (and hence the same segment
;                as the caller).
;

Entry $TJMP,,,nocheck
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        pop     bx
        push    cs
        push    bx

Procedure $TJMP,faronly
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing

        push    ds                      ; save callers ds
        push    bp
        mov     bp,sp                   ; reference stack

; We now have the following stack situation
;
;       Table segment(in caller code)
;       Table offset                    <-----SP at entry
;       Caller DS
;       BP                              <-----BP = SP

        mov     bx,[bp+6]
        mov     ds,bx                   ; (ds) = (caller) table segment
        mov     bx,[bp+4]               ; (bx) = (caller) table offset
        cmp     al,ds:[bx]              ; is index out of range?
        ja      short ftj1              ; yes, jmp to erroraddr

        xor     ah,ah                   ; ax = index
        inc     ax                      ; skip pass error entry
        add     ax,ax                   ; ax = offset into table - 1
        add     bx,ax                   ; ds:bx+1 -> target address offset

ftj1:   mov     bx,word ptr ds:[bx+1]   ; 1 is to account for maxindex byte
        mov     [bp+4],bx               ; setup target offset on stack
        pop     bp                      ; restore bp,ds
        pop     ds
        RETC                            ; far return to target address

EndProc $TJMP



;***    PathChrCmp - test for/translate a path character
;
;       ENTRY   (AL) = character to be tested
;
;       EXIT    Zero set if character is a path separator
;                       if so, (AL) will always be `\'
;               Zero reset if not a path separator, (AL) untouched
;
;       USES    AL, FL

Procedure PathChrCmp,HYBRID
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        cmp     al,'/'
        jz      short path1
        cmp     al,'\'
        RETC

path1:  mov     al,'\'
        RETC
EndProc PathChrCmp


;**     StrCmp - compare ASCIZ DS:SI to ES:DI. Case INSENSITIVE. '/' = '\'
;               Strings of different lengths don't match.  This routine
;               properly handles DBCS characters.
;   Inputs:     DS:SI - pointer to source string
;               ES:DI - pointer to dest string
;   Outputs:    Z if strings same, NZ if different
;   Registers modified: NONE

Procedure StrCmp,HYBRID
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
        SaveReg <SI,DI,AX>

Cmplp:  LODSB
        TestDBCS Cmp_1                  ; double byte character?
        JNZ     short Cmp_1             ; no, continue below
        CMP     AL, ES:[DI]
        JNZ     short PopRet
        LODSB                           ; compare training byte
        INC     DI
        CMP     AL, ES:[DI]
        JNZ     short PopRet
        INC     DI
        JMP     short Cmp_4

Cmp_1:  CALL    UCase                   ; convert to upper case
        CMP     AL,'/'                  ; convert / to \
        JNE     short Cmp_2
        MOV     AL,'\'
Cmp_2:  MOV     AH,AL
        MOV     AL,ES:[DI]
        INC     DI
        CALL    UCase                   ; convert to upper case
        CMP     AL,'/'                  ; convert / to \
        JNE     short Cmp_3
        MOV     AL,'\'
Cmp_3:  CMP     AH,AL
        JNZ     short PopRet            ; Strings dif
Cmp_4:  OR      AL,AL
        JNZ     short Cmplp             ; More string

PopRet: RestoreReg  <AX,DI,SI>
        RETC
EndProc StrCmp


;**     StrCpy -        copy an ASCIZ string from DS:SI to ES:DI
;                       also uppercases and converst / to \
;
;**     FStrCpy -       copy an ASCIZ string from DS:SI to ES:DI.
;                       no modification of characters.
;
;**     FStrCpy32 -     copy an ASCIZ string from DS:ESI to ES:EDI.
;                       no modification of characters.
;
;   Inputs:     DS:[E]SI - pointer to source string
;               ES:[E]DI - pointer to destination string
;   Outputs:    ES:[E]DI point byte after nul byte at end of dest string
;               DS:[E]SI point byte after nul byte at end of source string
;               Carry set if last byte copies is DBCS
;   Registers modified: [E]SI,[E]DI

Procedure StrCpy,<HYBRID,far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
        PUSH    AX
        TestDBCSProcess                 ; DBCS active? (jmp - no)
        jnz     short CPYL_3
        PUSH    BX                      ; yes
        XOR     BL,BL                   ; clear DBCS flag
CPYLoop:
        SHL     BL, 1                   ; assume last char DBCS
        LODSB
        TestDBCSLeadByte                ; double byte character?
        JNZ     short CPYL_1            ; no -- jump down
B1EMac  stosb                           ; copy lead byte
        LODSB                           ; get trailing byte
        INC     BL                      ; remember last character is DBCS
        JMP     short CPYL_2

CPYL_1: CALL    UCase                   ; upper case single byte characters
        CMP     AL,'/'                  ; convert / to \
        JNE     short CPYL_2
        MOV     AL,'\'
CPYL_2:
B1EMac  stosb
        OR      AL,AL
        JNZ     short CPYLoop
        AND     BL,2                    ; get second to last character flag
        SUB     AL,BL                   ; set/reset carry flag for DBCS
        POP     BX
        POP     AX
        RETC

CPYL_3: LODSB                           ; not DBCS -- get data byte
        CALL    UCase                   ; upper case single byte characters
        CMP     AL,'/'                  ; convert / to \
        JNE     short CPYL_4
        MOV     AL,'\'
CPYL_4:
B1EMac  stosb
        OR      AL,AL
        JNZ     short CPYL_3
        POP     AX
        RETC
EndProc StrCpy


Procedure FStrCpy,<near,faronly,far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
        PUSH    AX
        TestDBCSProcess                 ; DBCS active?
        jnz     short FCPY_2            ; DBCS active?
        PUSH    BX                      ; yes
        XOR     BL,BL                   ; clear DBCS flag
FCPYLoop:
        SHL     BL,1                    ; zero bit in BL => SBCS
        LODSB
        TestDBCSLeadByte                ; double byte character?
        JNZ     short FCPY_1            ; no, continue below
B1EMac  stosb                           ; store leading byte
        LODSB                           ; get trailing byte
        INC     BL                      ; remember last char was DBCS
FCPY_1:
B1EMac  stosb
        OR      AL,AL                   ; done yet?
        JNZ     short FCPYLoop
        AND     BL,2                    ; look at character before null.
        SUB     AL,BL                   ; set/rest carry flag for DBCS
        POP     BX
        POP     AX
        RETC

FCPY_2: LODSB                           ; no -- get non-DBCS data byte
B1EMac  stosb                           ; store byte
        OR      AL,AL                   ; done yet?
        JNZ     short FCPY_2
        POP     AX
        RETC
EndProc FStrCpy


Procedure FStrCpy32,<near,faronly,far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
        PUSH    AX
        TestDBCSProcess                 ; DBCS active?
        jnz     short FCPY32_2          ; DBCS active?
        PUSH    BX                      ; yes
        XOR     BL,BL                   ; clear DBCS flag
FCPY32Loop:
        SHL     BL,1                    ; zero bit in BL => SBCS
        LODS    byte ptr [esi]
        TestDBCSLeadByte                ; double byte character?
        JNZ     short FCPY32_1          ; no, continue below
B1EMac  stos    byte ptr es:[edi]       ; store leading byte
        LODS    byte ptr [esi]          ; get trailing byte
        INC     BL                      ; remember last char was DBCS
FCPY32_1:
B1EMac  stos    byte ptr es:[edi]
        OR      AL,AL                   ; done yet?
        JNZ     short FCPY32Loop
        AND     BL,2                    ; look at character before null.
        SUB     AL,BL                   ; set/rest carry flag for DBCS
        POP     BX
        POP     AX
        RETC

FCPY32_2: LODS  byte ptr [esi]          ; no -- get non-DBCS data byte
B1EMac  stos    byte ptr es:[edi]       ; store byte
        OR      AL,AL                   ; done yet?
        JNZ     short FCPY32_2
        POP     AX
        RETC
EndProc FStrCpy32


;***    UCase - Upper case the letter in AL.
;
;       ASCII characters are translated to upper case.  Characters
;       above 128d are properly case mapped.
;
;       ENTRY   AL - a character
;       EXIT    AL - if AL was lower case, it's now upper
;       USES    AL, Flags

Procedure UCase,<hybrid, far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
        cmp     al,'a'                          ; below latin range?
        jb      short uc9                       ; yes, go skip any conversion
        cmp     al,'z'                          ; above latin range?
        ja      short uc1                       ; yes, go check internat table
        add     al,'A'-'a'                      ; upper case latin chars
        RETC                                    ; go return, conversion complete

uc1:    cmp     AL,80h                          ; in international set?
        jb      short uc9                       ; no, go skip conversion
        cmp     [CP_CaseMapTbl].Segmt,0         ; is there case map table?
        jz      short uc9                       ; no, go skip conversion
        SaveReg <DS,BX>                         ; save scratch regs
        lds     bx,[CP_CaseMapTbl]              ; (DS:BX)->casemap table
        xlat                                    ; convert character
        RestoreReg <BX,DS>                      ; restore scratch regs
uc9:    RETC
EndProc UCase


HIGH2CODE ends


        FARCODE HIGH3
HIGH3CODE segment


;       DHGetMessage16 - get message through DosHlp
;
;       This function retrieves the specifies message through
;       the DosHlp interface.
;
;       This function is called when the message retrieval
;       system is unavailable.  The DosHlp module is always
;       resident in memory so no swapping or disk access
;       is needed to perform the message retrieval.
;
;       ENTRY:
;               AX      - message number
;
;       EXIT:
;               DS:SI   -> message string
;               C       -> Clear if message number found
;                       -> Set if message number not found

Procedure DHGetMessage16,far16
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing

        DosContext <ds>
        call    DHF.DosHlpProtGetMessage

        RETC
EndProc DHGetMessage16


;***EP  OutMessage - print message with replacement arguments
;
;       Outputs a message replacing the %1, %2, etc. with the
;       replacement strings passed. Has the following limitations:
;
;       * The order of the replacement parameter numbers in
;         the message text is ignored. The first replacement
;         string is printed at the first "%n" conbination no
;         matter what number n is.
;
;       * A "%" and the character after it is aways treated
;         like a replacement parameter.
;
;       * If there are less than the number of replacement strings
;         passed as there are "%n" replacement parameters in the
;         message text, no replacement string or "%n" is printed.
;
;       * This routine will not work correctly if the last character
;         of the message text is a "%".
;
;       ENTRY   (ES:EDI) = replacement strings (each NULL terminated).
;               (AX) = message number.
;               (BX) = number of replacement strings
;       EXIT    If carry set, message number not in table.
;               If carry clear, message printed.
;       USES    AX, BX, CX, SI, DI, Flags

Procedure OutMessage,<near, faronly, far16>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing

        SaveReg <ds>
        DosContext <ds>
        call    DHF.DosHlpProtGetMessage; (ds:si) = message length + text
        jc      short omg7              ; exit if couldn't find the message
        lodsw
        mov     cx, ax                  ; (cx) = message length

omg3:   lodsb                           ; (AL) = character from message text
        cmp     al,'%'
        jne     short omg5              ; jump if not a replacement character
        lodsb                           ; (AL) = replacement string number
        dec     cx

        or      bx,bx
        jz      short omg6              ; jump if are no more replacement strs
        dec     bx

;       (ES:EDI) = pointer to replacement strings (each one
;       terminated by a null).

omg4:   mov     al,es:[edi]             ; (AL) = character from replacement str
        inc     edi
        or      al,al
        jz      short omg6              ; jump if end of replacement string
        CALLFAR ByteToScrnDM
        jmp     short omg4

omg5:   CALLFAR ByteToScrnDM
omg6:   loop    omg3                    ; jump to beginning of loop
        clc                             ; no error
omg7:   RestoreReg <ds>
        RETC
EndProc OutMessage


HIGH3CODE ends


        FARCODE HIGH32
_TEXT   SEGMENT

;***    HexToAscii32 - convert a word into an ASCII hex number.
;***    ByteToAscii32 - convert a byte into an ASCII hex number.
;
;       ENTRY:  AX/AL contains the number to be converted
;               ES:EDI point to destination
;
;       EXIT:   ES:EDI updated
;
;       USES:   AX, EDI, Flags

Procedure HexToAscii32
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
        push    eax
        mov     al,ah
        call    ByteToAscii32
        pop     eax
Entry ByteToAscii32
        push    eax
        shr     al,4
        call    NibbleToAscii32
        pop     eax
Entry NibbleToAscii32,,local
        and     al,0fh
        add     al,90h
        daa
        adc     al,40h
        daa
        or      al,20h            ; to lower case
B1EMac  stos    byte ptr es:[edi] ; store the ascii character
        RETC
EndProc HexToAscii32


;***EP  _IntToDecAscii - converts an integer to an ASCII decimal number
;
;       extern char *IntToDecAscii(int, char *);
;
;       The ascii decimal number has leading zeros removed, and may contain
;       as many as 11 bytes, including the trailing nul byte.  An 11 byte
;       buffer is required!
;
;       May be called with DS,ES FLAT or non-FLAT.
;
;       ENTRY   [wta_int]    = the number to be converted
;               [wta_pchbuf] = DS,ES-relative destination buffer pointer
;       EXIT    (EAX) = pointer to nul terminating byte
;       USES    EAX, ECX, EDX, Flags

Procedure _IntToDecAscii,,,,esp
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
    ArgVar wta_int,DWORD
    ArgVar wta_pchbuf,DWORD
        SaveReg <esi,edi>
        mov     edi,[wta_pchbuf]
        mov     eax,[wta_int]
        CALL16  IntToDecAscii
        mov     eax,edi
        RestoreReg <edi,esi>
        RETC
EndProc _IntToDecAscii


;***EP  _DHGetMessage32 - get message through DosHlp
;
;       This function retrieves the specifies message through
;       the DosHlp interface.
;
;       This function is called when the message retrieval
;       system is unavailable.  The DosHlp module is always
;       resident in memory so no swapping or disk access
;       is needed to perform the message retrieval.
;
;       char *DHGetMessage32( msgno)
;
;       ENTRY:
;               msgno   - message number
;
;       EXIT:
;               pmsg   - address of message pointer (0 if not found)
;
;       PSEUDO CODE:
;
;       begin
;           move message number to EAX and call DHGetMessage16
;           if error
;               return error
;           else
;               load and extend ds:di into esi,edi
;               convert 16:16 to 0:32 with _SELVirtToLin
;               if invalid
;                   return error
;               else
;                   return error and correct message address
;               endif
;           endif
;       end

Procedure _DHGetMessage32
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
ArgVar  msgno, DWORD

        EnterProc
        SaveReg <esi,edi,ds>
        mov     EAX,msgno               ;Prepare message number
        CALL16  DHGetMessage16          ;DS:SI -> address of message
        jc      short dhgm30            ;No message found - return 0
dhgm10:
        mov     di,ds                   ;Move and extend segment
        FlatContext <ds,es>             ;needed for _SELVirtToLin
        movzx   edi,di
        movzx   esi,si                  ;Extend offset
        CCALL32 _SELVirtToLin <esi,edi> ;EAX -> Linear address of message
        cmp     eax,-1                  ;Set carry flag if unequal
        jne     short dhgm40            ;No error - skip error return

dhgm30:
        xor     eax,eax                 ;return error
dhgm40:
        RestoreReg <ds,edi,esi>
        LeaveProc

        RETC
EndProc _DHGetMessage32


;161596
Procedure _GetVersionInfo32
   assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing

	EnterProc
	SaveReg <esi,edi,ds,es,ebx>
	CALL16  w_Get_Version
	RestoreReg <ebx,es,ds,edi,esi>
	LeaveProc

	RETC
EndProc _GetVersionInfo32

_TEXT   ENDS

        end
