;       SCCSID = @(#)inimsg.asm 6.2 92/01/22
        page    78,132
SCCSID  equ     "@(#)inimsg.asm 6.2 92/01/22"

;                 IBM/Microsoft Confidential
;
;           Copyright (c) IBM Corporation  1987, 1990
;           Copyright (c) Microsoft Corp.  1987, 1990
;                     All Rights Reserved
;  Change History
;
;  106726  ChangeTeam  10/02/92  Prevent GPF by explicitly declaring data segment

.xlist
include dosseg.inc
include basemaca.inc
include osmaca.inc
include basemid.inc
include doshlp.inc
include fnoinit.inc
include inisms.inc
include mi.inc
include sicg.inc
include struc.inc
.list

        ModName  , FNO_INIMSG_ASM
        CpuMode 386

        ReferGlobal     DHF,dword

        ReferInitGlobal error_messages_displayed,byte
        ReferInitGlobal message_buffer,byte
        ReferInitGlobal message_length,word
        ReferInitGlobal pBanner,dword
        ReferInitGlobal pDosGetMessage,dword
        ReferInitGlobal pDosPutMessage,dword
        ReferInitGlobal tbl_kernel_message,byte
        ReferInitGlobal tbl_system_stopped,byte
        ReferInitGlobal syiLostMessages,word
        ReferInitGlobal decimalascii,byte
        ReferInitGlobal tbl_lost_messages,byte
        ReferInitGlobal tbl_prompt,byte

        EXTRNFAR        WrtToScrn

        extrn           ProtModeIC:near
        extrn           RealModeIC:near


INITDATA segment
badstring       db      '<???>', 0
INITDATA ends

INITBSS segment
;       Selector for SysInit Message Segment

        public  syiMessageSelector
syiMessageSelector dw ?
        public  syiSavedMessageIndicator
syiSavedMessageIndicator db 0

INITBSS ends


        FARCODE INIT
INITCODE segment


;***LP  syiDisplayMessage - Display a message
;
;       Retrieve and display an error message and prompt the user to read
;       an error message.
;
;       ENTRY   (DS:SI) = Address of SysInit Message Table (SMT)
;                         containing the following data:
;                          SMT_Message_ID = message ID
;                          SMT_No_Vars    = number of insertion items
;                          SMT_Var_Ptr    = address of ASCIIZ string
;                                           (repeated n times)
;       EXIT    NONE
;       USES    Flags
;
;       call syiRetrieveMessage(message table) to retrieve the message
;
;       if (this is an error message)
;           indicate an error message was displayed
;       call syiShowMessage(message buffer) to display the message
;       call syiPauseOnError to display "press Enter key to continue"
;           if an error message was displayed

Procedure syiDisplayMessage,near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

        SaveReg <ds,es>
        pusha

;       Retrieve the message text and fill in variable text

        call    syiRetrieveMessage

;       See if this is an error message
;       NOTE: An error message is always prefixed with the message
;             identifier (SYSnnnn:), while an informational or
;             prompt message starts with the message text. Any
;             message which contains a colon in the 8th character
;             position is assumed to be an error message

        DosInitContext ds
     ;Don't interpret saved messages (DevHlp_SaveMessage) as errors
     ;Even if they have a colon as the 7th byte.
        .if     <syiSavedMessageIndicator eq 0> and
        .if     <[message_buffer+7] eq ':'>
          mov   [error_messages_displayed],1
        .endif

        call    syiShowMessage          ; Display the message on the screen

;       If any error messages have been displayed and prompting is desired and
;       possible at this time, prompt the user to press Enter key to continue.
;       BUGBUG: this has always been disabled for ring 0 callers:
;       call    syiPauseOnError

        popa
        RestoreReg <es,ds>
        ret
EndProc syiDisplayMessage


;***LP  syiRetrieveMessage - Retrieve a message
;
;       Retrieve a message, either by using the message retriever, or by
;       supplying a pre-assembled message.
;
;       INPUT   (DS:SI) = Address of SysInit Message Table (SMT)
;                         containing the following data:
;                          SMT_Message_ID = message ID
;                          SMT_No_Vars    = number of insertion items
;                          SMT_Var_Ptr    = address of ASCIIZ string
;                                           (repeated n times)
;       EXIT    message_buffer contains the message
;               message_length contains the length of the message
;       USES    Flags
;
;       copy the pre-assembled message text to the buffer,
;       filling in the first insertion item if %n is encountered
;       calculate length of message

Procedure syiRetrieveMessage,near
    assume ds:DOSINITDATA, es:nothing, fs:nothing, gs:nothing, ss:nothing

        LocalVar        SaveDS, WORD
        EnterProc

        SaveReg <ds,es>
        pusha

;       The Message Retriever is not available from ring 0; construct the
;       message from the pre-assembled message text and the insertion text.

        mov     ax,ds:[si].SMT_Message_ID       ; pick up message ID
        mov     bx,si                           ; save address of message table

;       Select the appropriate pre-assembled message

        SaveReg <ds>
        .if     <ax eq MSG_SYSINIT_BANNER>
            lds  si,[pBanner]                   ; (ds:si) = message text
        .else
            DosContext <ds>
            call  DHF.DosHlpProtGetMessage      ; (ds:si) = message text
        .endif

;       Move the pre-assembled message to the message buffer.
;       NOTE: This section of code uses the LOOP instruction and the CX
;       register to count the number of bytes moved to the message buffer.
;       When the loop is complete, CX contains the NEGATIVE # of bytes moved.

        DosInitContext es
        mov     di,offset message_buffer
        add     si,2                    ; point to message text
        xor     cx,cx                   ; clear text counter
        cld
        .repeat                         ; copy the entire message
          lodsb                         ; pick up a byte
          .leave <al eq 0>              ; quit if end of message
          .if <al eq '%'>               ; check for fill-in item (%n)

;           Copy the fill-in item from the message table

            lodsb                       ; pick up the next character
            .if <al ae '1'> and         ; is it a digit?
            .if <al be '9'>
              sub al,'0'                ; yes, it is OK
              xor ah,ah
              mov SaveDS, ds
              RestoreReg <ds>
              SaveReg <ds,si>
              .if <ds:[bx].SMT_No_Vars ae ax>
                mov si,ax               ; find correct fill-in item
                dec si
                shl si,2
                lds si,dword ptr ds:[bx+si+size SMT].SMT_Var_Ptr
                .repeat                 ; copy fill-in item to the
                  lodsb                 ; output buffer
                  .leave <al eq 0>
                  stosb
                .loop
              .endif
              mov ds, SaveDS
              RestoreReg <si>
            .endif
            inc cx                      ; adjust character count for the
                                        ; % sign (which was not copied)
          .else                         ; not a fill-in item
            stosb                       ; store the character in the buffer
          .endif
        .loop                           ; process the entire message
        RestoreReg <ds>

;       Calculate the total length of the message

        neg     cx
        mov     [message_length],cx

        popa
        RestoreReg <es,ds>
    assume ds:nothing,es:nothing
        LeaveProc
        ret
EndProc syiRetrieveMessage


;***LP  syiShowMessage - call WrtToScrn to display a message
;
;       INPUT   [message_buffer] contains the message
;               [message_length] contains the length of the message
;       EXIT    NONE
;       USES    Flags

Procedure syiShowMessage,near
    assume ds:DOSINITDATA, es:nothing, fs:nothing, gs:nothing, ss:nothing
        SaveReg <ax,edx,si>

        mov     edx,offset message_buffer; (ds:edx) -> message buffer
        mov     cx,[message_length]      ; (cx) = length of message
        CALLFAR WrtToScrn

        RestoreReg <si,edx,ax>
        ret
EndProc syiShowMessage


;***EP  SYISaveMessage - save message
;
;       Save the message ID and variable text of a message to be displayed
;       when the System Initialization Process is running.
;
;       ENTRY   (DS:SI) = address of SysInit Message Table (SMT)
;                         containing the following data:
;                          SMT_Message_ID = message ID
;                          SMT_No_Vars    = number of insertion items
;                          SMT_Var_Ptr    = address of ASCIIZ string
;                                           (repeated n times)
;       EXIT    NONE
;       USES    Flags
;       EFFECTS syiLostMessages = number of messages which did not fit in the
;                                 message segment
;
;       if (the information does not fit in the message segment)
;           increment count of lost messages
;           return
;
;       copy the message id and number of items to the message segment
;
;       for (each item)
;           fill in the item pointer in the copied message table
;           copy the ASCIIZ string to the message segment

Procedure SYISaveMessage,<near,far16>
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
    LocalVar ssm_newsmt,WORD

        EnterProc
        SaveReg <ds,es>
        pusha

;       Allocate space for the new message table

        DosInitContext <es>
        mov     es,[syiMessageSelector]
        assume  es:nothing
        mov     di,es:[0].SMS_Free      ; point to free area
        mov     [ssm_newsmt],di         ; save address of the new SMT
        add     di,offset SMS_SMT       ; point to the new SMT
        mov     dx,di

;       Copy the message table to the message segment

        mov     cx,ds:[si].SMT_No_Vars  ; calculate size of SMT +
        shl     cx,1                    ; insertion item pointers
        add     cx,(size SMT)/2

        mov     ax,di                   ; see if it fits in the
        add     ax,cx                   ;         message segment
        add     ax,cx
        .if     <ax ge es:[0].SMS_Size>
          DosInitContext <es>
          inc   [syiLostMessages]
          assume es:nothing
          jmp   save_message_exit
        .endif

        SaveReg <si>
        cld                             ; copy the SMT + insertion
        rep     movsw                   ; item pointers
        RestoreReg <si>

;       Copy each insertion item string to the message segment

        mov     cx,ds:[si].SMT_No_Vars
        .if     <ncxz>
          xor   bx,bx
          .repeat
            SaveReg <ds,si>
            xchg dx,di                  ; put address of string into
            mov es:[di+size SMT].[bx].SMT_Var_Ptr.Segmt,es ; copied
            mov es:[di+size SMT].[bx].SMT_Var_Ptr.Offst,dx ; SMT
            xchg dx,di
            lds si,ds:[si+size SMT].[bx].SMT_Var_Ptr ; point to item
            mov ax,ds
            or  ax,si
            .if <zero ax>
                DosInitContext ds
                mov     si,offset badstring
            .endif
            .repeat                     ; copy string
              .if <di ge es:[0].SMS_Size> ; make sure it fits
                RestoreReg <si,ds>
                DosInitContext <es>
;106726                inc [syiLostMessages]
                inc es:[syiLostMessages]                                 ;106726
                assume es:nothing
                jmp save_message_exit
              .endif
              lodsb
              stosb
            .until <al eq 0>
            RestoreReg <si,ds>
            add bx,size SMT_Var         ; point to next insertion item
          .loop
        .endif

;       Update the free space pointer in the message segment and link
;       the new message table to the last SMT in the segment

        mov     es:[0].SMS_Free,di      ; save new free area pointer

        mov     di,[ssm_newsmt]         ; pick up address of the new SMT
        mov     si,es:[0].SMS_Last      ; point to last SMT in the segment
        .if     <nonzero si>
          mov   es:[si].SMS_Next,di     ; chain the new SMT to the last SMT
        .else
          mov   es:[0].SMS_First,di     ; this is the first SMT
        .endif
        mov     es:[di].SMS_Next,0      ; clear forward pointer in the SMT
        mov     es:[0].SMS_Last,di      ; make this the last SMT

save_message_exit:
        popa
        RestoreReg <es,ds>
        LeaveProc
        ret
EndProc SYISaveMessage,NoCheck


;***LP  SYISaveMessageID - save message identifier
;
;       Save the identifier of a message to be displayed when the SysInit
;       Process is running
;
;       ENTRY   (AX) = message identifier
;       EXIT    NONE
;       USES    Flags
;
;       call SYISaveMessage(message ID,0 insertion items) to save the messageID

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

        SaveReg <ds,es>
        pusha

;       Save the message ID for later use

        DosInitContext <ds>
        mov     si,offset tbl_kernel_message
        mov     ds:[si].SMT_Message_ID,ax
        call    SYISaveMessage

        popa
        RestoreReg <es,ds>
        ret
EndProc SYISaveMessageID

;***LP  syiDisplaySavedMessages - Display saved messages
;
;       To display all messages which were previously generated during
;       System Initialization.
;
;       ENTRY   syiMessageSelector contains the message segment selector
;       EXIT    NONE
;       USES    Flags
;
;       for (each message in the message segment)
;         call syiDisplayMessage(message table) to display the message
;
;       if (any messages were lost)
;         call syiWordToDec to convert # of messages lost to ASCIIZ string
;         call syiDisplayMessage(lost messages)

Procedure syiDisplaySavedMessages
    assume ds:DOSINITDATA, es:nothing, fs:nothing, gs:nothing, ss:nothing

        SaveReg <ds,es>
        pusha

        DosInitContext ds

;       Display all saved messages

;       Indicate being called by SaveMessage
        mov      syiSavedMessageIndicator,1


        SaveReg <ds>
        mov     ds,[syiMessageSelector]
        ASSUME DS:nothing
        mov     di,ds:[0].SMS_First     ; point to first message table element

        DosInitContext <es>

        .while  <nonzero di>
          lea   si,[di].SMS_SMT         ; point to message table
          call  syiDisplayMessage       ; display this message
          mov   di,ds:[di].SMS_Next     ; point to next message table element
        .endwhile
        RestoreReg <ds>
        mov      syiSavedMessageIndicator,0

;       Tell the user if any messages were lost

        .if     <[syiLostMessages] ne 0>
          mov   ax,[syiLostMessages]
          mov   di,offset decimalascii
          push  ds
          pop   es
          call  syiWordToDec            ; *(es:di) = ascii(ax)
          mov   si,offset tbl_lost_messages
          call  syiDisplayMessage
        .endif

;       Initialize the header fields in the message segment
;       so we can save more messages and display them again if needed

        DosInitContext ds
        mov     ds,[syiMessageSelector]

        mov     ds:[0].SMS_Size,SMS_default_size
        mov     ds:[0].SMS_First,0
        mov     ds:[0].SMS_Last,0
        mov     ds:[0].SMS_Free,size SMS


        popa
        RestoreReg <es,ds>
        ret
EndProc syiDisplaySavedMessages


;***EP  SYIAbort - Abort the System Initialization Process
;
;       Display a fatal error message and halt the system.
;
;       ENTRY   (SI) = Address of SysInit Message Table (SMT)
;                      containing the following data:
;                       SMT_Message_ID = message ID
;                       SMT_No_Vars    = number of insertion items
;                       SMT_Var_Ptr    = address of ASCIIZ string
;                                        (repeated SMT_No_Vars times)
;       EXIT    Does not return.
;       EFFECTS The sytem is halted.
;
;       CANNOT and DOES NOT use the message retriever.
;
;       call syiDisplaySavedMessages to display saved messages
;       call syiDisplayMessage(message table) to display the message
;       call syiDisplayMessage(system stopped) to display the final message
;       Loop forever

Procedure SYIAbort,hybrid
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

;       Indicate the Message Retriever cannot be used

        DosInitContext ds
        mov     [pDosGetMessage].Segmt,0 ; indicate DosGetMessage and
        mov     [pDosPutMessage].Segmt,0 ; DosPutMessage are not loaded

;       Note: the last message displayed was the banner message and it
;       used the WrtToScrnClr function.  Since WrtToScrnClr was used for
;       this display, the screen will be cleared automatically when
;       displaying the error message below

        call    syiDisplaySavedMessages ; Display messages that were saved

        call    syiDisplayMessage       ; Display the error message

        mov     si,offset tbl_system_stopped
        call    syiDisplayMessage       ; Display the system stopped message

;       If it's early in ring 0 initialization, the interrupt controller
;       may be disabled.  Might be better to pause waiting for keyboard
;       input (if we can from ring 0 - via DosHlp?) and then reboot.

sab90:  sti                             ; enable interrupts
        jmp     short sab90             ; loop forever

EndProc SYIAbort

;***    syiWordToDec - converts a word to ASCII decimal number
;
;       The ascii decimal number has leading zeros removed, and may
;       contain as many as 6 bytes, including the trailing nul byte.
;       A 6 byte buffer is required!
;
;       ENTRY   (AX) contains the number to be converted
;               (ES:DI) point to destination
;       EXIT    (ES:DI) updated (points to trailing nul byte)
;       USES    AX, BX, DX, SI, DI, Flags

Procedure syiWordToDec
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
        mov     bx,10                   ; (BX) = radix (base 10)
        mov     si,di                   ; (SI) = base of result
        add     di,4
        mov     byte ptr es:[di+1],0    ; nul-terminate initial string
        std                             ; build initial string backwards

wtd10:  xor     dx,dx                   ; (DX),(AX) = (0:AX) / (BX)
        div     bx                      ; (AX) = quotient, (DX) = remainder
        xchg    al,dl                   ; (AL) = remainder
        add     al,'0'                  ; (AL) = ascii digit
        stosb                           ; drop in the character
        xchg    al,dl                   ; (AX) = quotient from last division
        or      ax,ax                   ; stop when (AX) == 0
        jnz     short wtd10

        cld                             ; copy string from beginning
        xor     dx,dx                   ; indicate drop leading '0's
        inc     di                      ; point to first byte stored
        xchg    si,di                   ; (SI) = initial, (DI) = final

wtd60:  lods    byte ptr es:[si]        ; grab a byte
        cmp     al,'0'
        jnz     short wtd70             ; copy if not '0'
        or      dx,dx
        jnz     short wtd70             ; or not leading
        cmp     byte ptr es:[si],0
        jnz     short wtd60             ; or not at end of initial string
wtd70:  inc     dx
        stosb
        or      al,al
        jnz     short wtd60             ; stop after trailing nul

        RETC
EndProc syiWordToDec


;***LP  syiPauseOnError - If possible, pause and wait for user input.
;
;       Prompt the user to read an error message.
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    Flags
;

Procedure syiPauseOnError
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

        SaveReg <ds,es>
        pusha

;       If any error messages have been displayed and prompting is
;       desired and possible at this time, prompt the user to
;       press the Enter key to continue

        DosInitContext ds
        .if     <[error_messages_displayed] ne 0>

          mov   si,offset tbl_prompt    ; display the prompt
          call  syiRetrieveMessage
          call  syiShowMessage

          DosContext ds
          NullContext  <es,fs,gs>,ax
          call    RealModeIC             ; switch to real mode
                                         ;  for KBD I/O
flush:                                    ; Flush the keyboard buffer

          mov     ah,1                    ; Check for key available
          int     16h                     ; Look for key
          jz      flushed

          xor     ah,ah                   ; Read available key
          int     16h                     ; Get the key
          jmp     short flush

flushed:

          mov     ah,0                    ; Read available key
          int     16h                     ; Wait for key press

          call         ProtModeIC         ;switch back to protect mode

          DosInitContext <ds,es>,ax
          mov   [error_messages_displayed],0 ; prompt was issued
        .endif

        popa
        RestoreReg <es,ds>
        ret
EndProc syiPauseOnError



INITCODE ends


        FARCODE HIGH32
CINIT_TEXT segment


;**EP   _SYISaveMessageSub - thunk for SYISaveMessage
;
;       _SYISaveMessageSub is a translator thunk between the 32-bit C
;       routine SYISaveMessage and the 16-bit assembler routine SYISaveMessage.
;
;       ENTRY   sms_pSMT - 16:16 pointer to an SMT
;       EXIT    none
;
Procedure _SYISaveMessageSub,near
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
    ArgVar sms_pSMT,dword
        EnterProc
        push    ebx
        push    esi
        push    edi
        push    ds

        lds     si,[sms_pSMT]           ; (ds:si) = addr of SMT packet
        CALL16  SYISaveMessage          ; preserves es

        pop     ds
        pop     edi
        pop     esi
        pop     ebx
        LeaveProc
        RETC
EndProc _SYISaveMessageSub


;**EP   _SYISaveMessageID
;
;       _SYISaveMessageID is a C callable wrapper for
;       SYISaveMessageID
;
;       ENTRY   [arg1] = message ID
;       EXIT    none

Procedure _SYISaveMessageID
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
        ArgVar  usMID,DWORD             ; message ID
        EnterProc
        push    edi                     ; old sysinit code might not
        push    esi                     ; save the high halves of
        push    ebx                     ; 32-bit registers

        mov     ax,[usMIDl]
        CALL16  SYISaveMessageID

        pop     ebx
        pop     esi
        pop     edi
        LeaveProc
        RETC
EndProc _SYISaveMessageID


CINIT_TEXT ends
        end
