;       SCCSID = @(#)monsuba.asm        6.1 91/04/26
;*****************************************************************************
;*****************************************************************************
; Assembler routines


INCL_DEF    EQU   1

   include basemaca.inc
   include basedef.inc

;***************************************************************************
;***************************************************************************
;***************************************************************************

extrn   _MonInstanceInit:FAR                                            ;MDLL

_DATA   SEGMENT PUBLIC 'DATA'                                           ;MDLL
_DATA   ENDS                                                            ;MDLL

CODE16C EQU     <_TEXT>                                                 ;MDLL
CODE16C SEGMENT PUBLIC 'CODE'
   ASSUME  CS: CODE16C, DS: Nothing, ES: Nothing


;/******************* START OF SPECIFICATIONS *******************
;/*
;/* SUBROUTINE NAME:  ValidAddr
;/*
;/* FUNCTION: Verify that the specified address is 'valid', i.e.,
;/*           the selector and offset in that segment is valid.
;/*
;/* NOTES:
;/*
;/*
;/* ENTRY POINT:  ValidAddr
;/*    LINKAGE:  FAR or NEAR CALL
;/*
;/* INPUT:
;/*        Ver_Addr = 16:16 address to be verified
;/*
;/* EXIT-NORMAL:  AX = CTRUE =  address valid
;/*
;/* EXIT-ERROR:   AX = FALSE = address not valid (offset)
;/*               Trap D if selector is not valid
;/*
;/* EFFECTS:  Contents of AX register altered.
;/*
;/* INTERNAL REFERENCES: NONE
;/*
;/* EXTERNAL REFERENCES: NONE
;/******************* END OF SPECIFICATIONS *********************

Procedure _ValidAddr, near
        ArgVar  Ver_Addr, DWORD
.386p
        EnterProc
        PUSH    EBX
        PUSH    ECX
        PUSH    EDI
        PUSHF                           ; save flags

        MOV     ECX, Ver_Addr           ; get parameter
        MOV     DI, CX                  ; put offset into DI
        ROR     ECX, 10H                ; put selector into CX
; CX:DI contains selector:offset address to be validated

        PUSH    ES                      ; gp fault if invalid address
        MOV     ES,CX                   ; is passed to ValidAddr
        MOV     BX,ES:[DI]
        POP     ES

        LOADSL  bx, cx                  ; invoke 386 macro - see p1803
;*****  LSL  BX, CX                   ; validate selector (CX)

        JZ      valid_selector
addr_error:
        MOV     AX, FALSE
        JMP     va_end

valid_selector:
        CMP     DI, BX                  ; validate offset (DI)
        JA      addr_error
        MOV     AX, CTRUE

va_end:
        POPF                            ; restore flags
        POP     EDI
        POP     ECX
        POP     EBX
        LeaveProc
        RET
EndProc _ValidAddr


;/******************* START OF SPECIFICATIONS *******************
;/*
;/* SUBROUTINE NAME:  _force_a_fault
;/*
;/* FUNCTION: Create a GP fault.
;/*
;/* NOTES:
;/*
;/*
;/* ENTRY POINT:  _force_a_fault
;/*    LINKAGE:  NEAR CALL
;/*
;/* INPUT:
;/*
;/* EXIT-NORMAL:  never
;/*
;/* EXIT-ERROR:   never
;/*
;/* EFFECTS:  GP fault (trap D)
;/*
;/* INTERNAL REFERENCES: NONE
;/*
;/* EXTERNAL REFERENCES: NONE
;/******************* END OF SPECIFICATIONS *********************


Procedure _force_a_fault, near
   ;Do something to force a fault from RING3

   xor   ax, ax
   mov   es, ax
   mov   ax, es:[0]

   ret ; It will never return, but "RET" anyway.
EndProc _force_a_fault




;/******************* START OF SPECIFICATIONS *******************
;/*
;/* SUBROUTINE NAME:  ValidBuffs
;/*
;/* FUNCTION: Verify that:
;/*               -InBuff address is valid
;/*               -OutBuff address is valid
;/*               -Selectors are the same for both buffers
;/*               -buffers do not overlap unless they are equal
;/*               -buffers do not go past end of segment
;/*
;/* NOTES:  Since the monitor dispatcher only uses the first 4 bytes of
;/*         the user's buffer, we only validate length 4.
;/*
;/* ENTRY POINT:  ValidBuffs
;/*    LINKAGE:  NEAR CALL
;/*
;/* INPUT:
;/*        Ver_Addr = 16:16 address to be verified
;/*
;/* EXIT-NORMAL:  AX = CTRUE =  address valid
;/*
;/* EXIT-ERROR:   AX = FALSE = address not valid (offset)
;/*               Trap D if selector is not valid
;/*
;/* EFFECTS:  Contents of AX register altered.
;/*
;/* INTERNAL REFERENCES: NONE
;/*
;/* EXTERNAL REFERENCES: NONE
;/******************* END OF SPECIFICATIONS *********************

public _ValidBuffs

Procedure _ValidBuffs, near
   ArgVar  InBuff,  DWORD
   ArgVar  OutBuff, DWORD
.386p
   EnterProc


   PUSH     BX
   PUSH     SI
   PUSH     DX
   PUSH     DS
   PUSH     ES
   PUSHF
   PUSH     InBuff
   Call     _ValidAddr
   ADD      SP, 4
   OR       AX, AX          ;If AX= FALSE (0), then address is invalid
   JZ       invalid_buff

   PUSH     OutBuff
   Call     _ValidAddr
   ADD      SP, 4
   OR       AX, AX          ;If AX= FALSE (0), then address is invalid
   JZ       invalid_buff

   les      bx, InBuff
   mov      SI,bx           ;es:SI ->InBuff
   lds      bx, OutBuff     ;ds:bx ->OutBuff

   mov      dx, ds
   mov      ax, es          ;if InBuff's selector != OutBuff's selector
   cmp      dx, ax          ; then return error
   jne      invalid_buff

   mov      ax, si          ; si = offset at beginning of InBuff
   add      ax, 3           ; ax = offset at end of InBuff

   mov      dx, bx          ; bx = offset at beginning of OutBuff
   add      dx, 3           ; dx = offset at end of OutBuff

   cmp      si, bx
   je       vb_CheckLimit   ;If they are the same, then don't check overlap
   ja       Outfirst        ;jump if InBuff starts after OutBuff

;Inbuff starts first
   cmp      ax, bx
   jae      invalid_buff    ;if OutBuff starts inside of InBuff, then error

   mov      ax, dx          ; put ending offset for OutBuff(last buffer) in AX
   jmp      vb_CheckLimit

Outfirst:
   cmp      dx, SI
   jae      invalid_buff    ;if InBuff starts inside of OutBuff, then error
                            ; ending offset for InBuff(last buffer) is in AX

vb_CheckLimit:  ;ending offset for last buffer should be in AX

   LOADSL   bx, es          ; invoke 386 macro - see p1803
   cmp      ax, bx
   ja       invalid_buff    ;if ending buff ends past end of segment
                            ; then error

   MOV      AX, CTRUE       ;put CTRUE into AX
   JMP      vb_done

invalid_buff:
   XOR      AX, AX          ;put FALSE (0) into AX

vb_done:
   POPF
   POP      ES
   POP      DS
   POP      DX
   POP      SI
   POP      BX
   LeaveProc
   RET
EndProc _ValidBuffs



;*********************** START OF SPECIFICATIONS **************************
;
;   SUBROUTINE NAME: ENTER_DLL_CONFORM
;
;   DESCRIPTIVE NAME: enter DLL  (Push All, Set DS)
;
;   FUNCTION: Pushes all registers which may be corrupted by C,sets
;             DS to the DATA16       of the DLL
;
;   NOTES: Glue For Entering a DLL coded in C. This must be the first
;          thing called from a DLL API implemented in C to save the
;          requestor's environment.
;
;          This routine should only be called from the CODE16C segment
;
;          This MUST BE DECLARED AS: extern pascal ENTER_DLL_CONFORM()
;
;          C Calling Convention: ENTER_DLL_CONFORM();
;
;   ENTRY POINT: ENTER_DLL_CONFORM
;
;   INPUT:              none
;
;   EXIT-NORMAL:        N/A
;
;   EXIT-ERROR:         N/A
;
;   EFFECTS:    "pops" return address off stack, saves BX,CX,DX,
;               DS, and ES, pushes return address back onto stack,
;               set DS = to DATA16      , and returns; AX is corrupted
;
;   INTERNAL REFERENCES:    none
;
;   EXTERNAL REFERENCES:    none
;
;************************ END OF SPECIFICATIONS **************************

Procedure ENTER_DLL_CONFORM

    pop     ax                      ; Save RetIP

    push    0                       ; Push Dummy For EXIT_DLL_CONFORM

    push    ds                      ; Save Registers
    push    es
    push    bx
    push    cx
    push    dx
    push    si
    push    di

    mov     dx, _DATA               ; move _DATA
    mov     ds, dx                  ;  into DS

assume ds:_DATA

    jmp     ax                      ; Ret

EndProc ENTER_DLL_CONFORM


;*********************** START OF SPECIFICATIONS ***************************
;
;   SUBROUTINE NAME: EXIT_DLL_CONFORM
;
;   DESCRIPTIVE NAME: Exit DLL (Pop All registers)
;
;   FUNCTION: Pops all registers which were saved to prevent
;             corruption by C code
;
;   NOTES: Glue For Exiting a DLL coded in C. This must be the last
;          thing coded in a DLL API implemented in C to restore the
;          requestor's environment.
;
;          This routine should only be called from the CODE16C segment
;
;        This MUST BE DECLARED AS: extern pascal EXIT_DLL_CONFORM(unsigned int)
;
;          C Calling Convention: EXIT_DLL_CONFORM(API RC);
;
;   ENTRY POINT: EXIT_DLL_CONFORM
;
;   INPUT:      DW      return code
;
;   EXIT-NORMAL:        N/A
;
;   EXIT-ERROR:         N/A
;
;   EFFECTS:    reverses the effects of ENTER_DLL_CONFORM/EXIT_DLL_CONFORM
;               pops the return address off the stack, exchanges
;               with the indicated old registers on stack, sets
;               AX to the return code and restores the remaining
;               registers
;
;   INTERNAL REFERENCES:    none
;
;   EXTERNAL REFERENCES:    none
;
;************************ END OF SPECIFICATIONS ****************************

Procedure EXIT_DLL_CONFORM

    pop     bx              ; Save RetIP
    pop     ax              ; (ax) is now restored correctly

    push    bp
    mov     bp, sp

    mov     [bp+16], bx    ; Save RetIP in Dummy Storage from ENTER_DLL_CONFORM
    pop     bp

    pop     di              ;restore
    pop     si              ;  register
    pop     dx              ;    set
    pop     cx              ;
    pop     bx              ;
    pop     es              ;
    pop     ds              ;

    ret                     ; return

EndProc EXIT_DLL_CONFORM

;***************************************************************************

CODE16C ENDS
     END  _MonInstanceInit                                              ;MDLL
