; 
; Copyright (c) Microsoft Corporation.   All rights reserved.
;

include hal.inc
        
externdef __throwDispatcher:NEAR
externdef __throwDispatcherExplicitAddrAfter:NEAR
externdef ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z:NEAR

ifdef SINGULARITY_KERNEL
externdef ?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z:NEAR
externdef __throwBeyondMarker:NEAR
endif ; SINGULARITY_KERNEL

externdef __throwDivideByZeroException:NEAR
externdef __throwNullPointerException:NEAR
externdef __throwOverflowException:NEAR
externdef __throwStackOverflowException:NEAR

if EXCLUDED
externdef __checkFPStackDepth0:NEAR
externdef __checkFPStackDepth1:NEAR
externdef __checkFPStackDepth2:NEAR
externdef __checkFPStackDepth3:NEAR
externdef __checkFPStackDepth4:NEAR
externdef __checkFPStackDepth5:NEAR
externdef __checkFPStackDepth6:NEAR
externdef __checkFPStackDepth7:NEAR
endif ;; EXCLUDED

        align       8
$DBLMAXINT  DQ      041dfffffffc00000r          ; 2.14747e+009
$DBLMININT  DQ      0c1e0000000000000r          ; -2.14748e+009
$MAXLONG    DQ      07fffffffffffffffh
$MINLONG    DQ      08000000000000000h

;
;  __throwDispatcher(ecx=exception)
; 
; Description: this function is called to explicitly throw an exception.
; It assumes that the return address points to the instruction immediately
; after the one where the exception was thrown
;
; Arguments:
;    ecx = exception object
;    [esp] = the return address
; Effects:
;    1. Create ebp chain entry
;    2. Get return address and uses it to calculate the address of the 
;       instruction that threw the exception.
;    3. Looks up the appropriate handler and jumps to code to process it.

align 16

__throwDispatcher proc
        push        ebp          ; create ebp chain entry
        mov         ebp, esp     ; set new ebp
        mov         edx, [ebp+4] ; get return address
        push        ecx       ; save exception
        sub         edx, 1    ; adjust to point to throw location
        call        ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z
        pop         ecx  ; restore exception
        pop         ebp       ; remove ebp chain
        add         esp, 4    ; remove eip from the stack
;        mov        edx is already ok
        jmp         __throwDispatcherHandler
__throwDispatcher endp

;
;  __throwDispatcherExplicitAddr (ecx=exception, edx=throwAddress)
;
; Description:
; This is to be used when the address where the exception occurred
; is passed in as an extra argument
;
; Arguments:
;    ecx = exception object
;    edx = address where the exception was thrown
;


align 16
__throwDispatcherExplicitAddr proc
        push        ecx  ; save exception
        call        ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z
        pop         ecx  ; restore exception
;        mov         edx is already ok
        jmp         __throwDispatcherHandler
__throwDispatcherExplicitAddr endp

;
;  __throwDispatcherExplictAddrAfter (ecx=exception, edx=throwAddress)
;
; Description:
;    This is to be used when the address of the instruction immediately after
;    the one that actually threw the exception is passed as an argument.
;
; Arguments:
;    ecx = pointer to exception object being thrown
;    edx = address of the instruction following the one where
;          the exception was thrown
;
; This is used, for example, in stack unwinding, where edx is the
; return address on the stack for the current procedure. 
;
; Stack unwinding occurs when the current procedure does not have a handler
; for the routine.   The idea is to pop the stack frame and treat the call
; instruction in the caller as though it threw.    We only have the return
; address, though, which points to the instruction *after* the call.   
;

align 16
__throwDispatcherExplicitAddrAfter proc
ifdef SINGULARITY
;        int 3                   
        push        ecx
        push        edx
        mov         ecx, edx
        call        ?g_IsUnlinkStack@Class_System_Exception@@SI_NPAUuintPtr@@@Z
        pop         edx
        pop         ecx
        test        al, al      
        je          normal
        mov         eax, ecx                       ; save exception type
        mov         ecx, edx            
        mov         edx, [ebp+4]                   ; save the return addr in caller
        mov         [ebp+4], afterUnlinkStack      ; override return addr to instr after
        jmp         ecx                            ; unlink stack which saves eax, edx
afterUnlinkStack:      
        mov         ecx, eax                       ; restore return addr in caller
normal:
        ; Have we reached a kernel->process or process->kernel boundary?
        ; Call Exception.CheckKernelProcessBoundary(esp, ebp, exception)
        push        ecx
        push        edx
        push        ecx      ; arg 3 (exception)
        mov         ecx, esp ; arg 2 (ebp)
        mov         edx, ebp ; arg 1 (esp)
        call        ?g_CheckKernelProcessBoundary@Class_System_Threading_Thread@@SIPAUuintPtr@@PAU2@0PAUClass_System_Exception@@@Z
        pop         edx
        pop         ecx
        ; A non-zero return value means we reached a boundary.
        test        eax, eax      
        jz          normal2
ifdef SINGULARITY_PROCESS
        ;; The process's exception reached the kernel's frames.
        ; Return gracefully to the kernel (i.e. jump to the kernel's
        ;   return address), and the kernel's popStackMark
        ;   will check uncaughtFlag and throw a new exception.
        jmp edx
endif ; SINGULARITY_PROCESS
ifdef SINGULARITY_KERNEL
; There are 3 control paths that merge here:
;  (1) When Thread.cs stops a process mode thread, it sets eip to point here.
;  (2) When Thread.cs stops a blocked kernel thread, it sets eip to point here.
;  (3) The stack unwinder falls through to this case when a kernel exception
;      reaches a process's frames.
; For control path (3), a finally block puts us in a GC safe state
; just before we reach here.
; We also expect to be in the GC safe state most of the
; time for control paths (1) and (2), but
; because pushStackMark and popStackMark are not atomic operations,
; we cannot be 100% sure that we're in the GC safe state.
;   eax: kernel->process or kernel->kernel marker
;   ecx: exception
__throwBeyondMarker LABEL NEAR
        push ecx
        push eax
        ; Leave the GC safe state (if we're actually in it)
        call ?g_RestoreMutatorControlIfNeeded@Class_System_GCs_Transitions@@SIXXZ
        pop eax
        pop ecx
;   eax: kernel->process or kernel->kernel marker
;   ecx: exception
        ; Restore state from the marker, skipping over any intermediate frames.
        ; Keep the original exception in ecx.
        ; Get the return address for the frame beyond the marker into edx:
        mov         edx, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom
        mov         edx, dword ptr [edx - 4]
        ; Restore the kernel's ebx, edi, esi, ebp from the marker:
        mov         ebx, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX
        mov         edi, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI
        mov         esi, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI
        mov         ebp, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP
        ; Keep a copy of transition record (eax) and oldTransitionRecord
        push        eax
        push        dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord
        ; Read _stackBottom (the new esp) from the kernel->process marker:
        mov         eax, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom
        ; Save ecx, edx to new stack -- this may overwrite the transition record,
        ; so don't read any more fields from the transition record after this point.
        mov         [eax - 4], ecx  ; (exception)
        mov         [eax - 8], edx  ; (return address)
        ; Set up the two arguments to DiscardStackSegments
        pop         edx  ; (oldTransitionRecord)
        pop         ecx  ; (marker)
        ; Restore the kernel's esp from the kernel->process marker:
        lea         esp, [eax - 8]
        ; Free any stack segments that we skipped over:
        ;; TODO: are we sure there's enough stack space to call this?
        call        ?g_DiscardSkippedStackSegments@Class_System_Threading_Thread@@SIXPAUStruct_System_GCs_CallStack_TransitionRecord@@0@Z
        pop         edx  ; (return address)
        pop         ecx  ; (exception)
endif ; SINGULARITY_KERNEL
normal2:
endif
        push        ecx  ;  save exception
        dec         edx
        call        ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z
        pop         ecx  ; restore exception
;        mov         edx is already ok
        jmp         __throwDispatcherHandler
__throwDispatcherExplicitAddrAfter endp

;  __throwDispatcherHandler (eax=frameSetupInfo or exceptionType,
;                            ecx=exception,
;                            edx=spillSize,frameSetupSize, or handlerAddress
;
; Description:
;    After the exception table entry has been found, the values are passed here
;    for processing.  This method simply checks for the easy case of an explicit
;    handler (known if the exceptionType is given <-- low bit is zero).
;    In this case it simply jumps to the handler.  Otherwise it passes the
;    information along to __throwDispatcherUnwind.
;
; Arguments:
;    If low bit of eax is set:
;        eax = frame setup information
;        ecx = exception object
;        edx = spill size excluding callee-save register saves
;              or offset from ebp to end of callee-save register saves
;    Otherwise:
;        eax = exception type (unused)
;        ecx = exception object
;        edx = handler address

align 16
__throwDispatcherHandler proc
        test        eax, 1
        jne         __throwDispatcherUnwind
        ;; ecx=exception, edx=handler
        jmp         edx
__throwDispatcherHandler endp

;  __throwDispatcherUnwind (eax=frame setup info, ecx=exception, edx=spill size
;
; Description:
;    This is the global unwind handler.  It is used to unwind a single stack
;    frame if there are no explicit handlers that catch an exception in a given
;    function.
;
; Arguments:
;    eax = frame setup information, must have low bit set
;    ecx = exception object
;    edx = spill size excluding callee-save register saves
;          or offset from ebp to end of callee-save register saves
;
; See tables\ExceptionTable.cs for details on these values

align 16
__throwDispatcherUnwind proc
        ;; eax=frame info
        ;; edx=spill size

        ;; obviously ebp isn't useful under frame pointer omission
        ;; but less obviously esp may be invalid if ebp is good
        ;; (e.g. under varargs we may not have known how many arguments to
        ;; pop; this is one reason why varargs turns off frame pointer omission)
        test        eax, 2h
        jne         esp_is_good
        ;; ebp_is_good
        add         edx, ebp
        mov         esp, edx
        jmp         esp_is_setup
esp_is_good:
        ;; pop spill slots
        add         esp, edx

esp_is_setup:

        ;; restore callee-saves and pop values from stack
        ;; (excludes ebp if used as the frame pointer)
        test        eax, 4h
        je          skip_edi_restore
        ;; restore edi
        pop         edi
skip_edi_restore:
        test        eax, 8h
        je          skip_esi_restore
        ;; restore esi
        pop         esi
skip_esi_restore:
        test        eax, 10h
        je          skip_ebp_restore
        ;; restore ebp
        pop         ebp
skip_ebp_restore:
        test        eax, 20h
        je          skip_ebx_restore
        ;; restore ebx
        pop         ebx
skip_ebx_restore:

        test        eax, 40h
        je          skip_jump_transition_record
        ;; jump over transition record
        add         esp, (SIZE Struct_System_GCs_CallStack_TransitionRecord)
skip_jump_transition_record:

        ;; restore ebp if it was used as the frame pointer
        test        eax, 2h
        jne         skip_frame_pointer_restore
        ;; restore frame pointer (esp == ebp already)
        pop         ebp
skip_frame_pointer_restore:

        ;; set edx=return address
        pop         edx

        ;; pop arguments
        shr         eax, 16
        add         esp, eax

        ;; At this point
        ;; ecx=exception, edx=return address
        ;; esi/edi/ebx/ebp/esp have been restored
        ;; eax is scratch

        ;; set up next handler search
        jmp         __throwDispatcherExplicitAddrAfter
__throwDispatcherUnwind endp

; 
; __throwDivideByZeroException: instantiate an divide-by-zero exception
; and throw it.
;
; Assumes edx points to the address after the one that threw.
;

align 16
__throwDivideByZeroException proc
        push ebx
        push esi
        mov ebx,edx  ; save address
if SINGLE_THREADED
        inc ?c_allocationInhibitGC@Class_System_GC@@2_NA
else ; SINGLE_THREADED
        lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA
endif ; SINGLE_THREADED
        mov ecx,offset ??_7System_DivideByZeroException@@6B@
        call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z
        mov esi,eax  ; save pointer to instance of exception
        mov ecx,eax  ; initialize instance
        call ?m__ctor@Class_System_DivideByZeroException@@SIXPAU1@@Z
if SINGLE_THREADED
        dec ?c_allocationInhibitGC@Class_System_GC@@2_NA
else ; SINGLE_THREADED
        lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA
endif ; SINGLE_THREADED
        mov ecx,esi
        mov edx,ebx
        pop esi
        pop ebx
        jmp __throwDispatcherExplicitAddr
__throwDivideByZeroException endp

; 
; __throwStackOverflowException: instantiate an StackOverflow exception
; and throw it.
;
; Assumes edx points to the address of the instruction that faulted
;

align 16
__throwStackOverflowException proc
        push ebx
        push esi
        mov ebx,edx  ; save address
if SINGLE_THREADED
        inc ?c_allocationInhibitGC@Class_System_GC@@2_NA
else ; SINGLE_THREADED
        lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA
endif ; MULTI_THREADED
        mov ecx,offset ??_7System_StackOverflowException@@6B@
        call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z
        mov esi,eax  ; save pointer to instance of exception
        mov ecx,eax  ; initialize instance
        call ?m__ctor@Class_System_StackOverflowException@@SIXPAU1@@Z
if SINGLE_THREADED
        dec ?c_allocationInhibitGC@Class_System_GC@@2_NA
else ; SINGLE_THREADED
        lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA
endif ; MULTI_THREADED
        mov ecx,esi
        mov edx,ebx
        call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z
ifndef SINGULARITY
        push esi
        push edx
        call ?ResetGuardPage@@YIXXZ
        pop edx
        pop eax
endif
        pop esi
        pop ebx
        mov ecx,eax
;        mov edx is already ok
        jmp edx
__throwStackOverflowException endp

; 
; __throwNullReferenceException: instantiate an NullReference exception
; and throw it.
;
; Assumes edx points to the address of the instruction that faulted
;

align 16
__throwNullReferenceException proc
        push ebx
        push esi
        mov ebx,edx  ; save address
if SINGLE_THREADED
        inc ?c_allocationInhibitGC@Class_System_GC@@2_NA
else ; SINGLE_THREADED
        lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA
endif ; SINGLE_THREADED
        mov ecx,offset ??_7System_NullReferenceException@@6B@
        call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z
        mov esi,eax  ; save pointer to instance of exception
        mov ecx,eax  ; initialize instance
        call ?m__ctor@Class_System_NullReferenceException@@SIXPAU1@@Z
if SINGLE_THREADED
        dec ?c_allocationInhibitGC@Class_System_GC@@2_NA
else ; SINGLE_THREADED
        lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA
endif ; SINGLE_THREADED
        mov ecx,esi
        mov edx,ebx
        pop esi
        pop ebx
        jmp __throwDispatcherExplicitAddr
__throwNullReferenceException endp

; 
; __throwDivideByZeroException: instantiate an divide-by-zero exception
; and throw it.
;
; Assumes edx points to the address of the instruction that faulted
;

align 16
__throwOverflowException proc
        push ebx
        push esi
        mov ebx,edx  ; save address
if SINGLE_THREADED
        inc ?c_allocationInhibitGC@Class_System_GC@@2_NA
else ; SINGLE_THREADED
        lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA
endif ; SINGLE_THREADED
        mov ecx,offset ??_7System_OverflowException@@6B@
        call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z
        mov esi,eax  ; save pointer to instance of exception
        mov ecx,eax  ; initialize instance
        call ?m__ctor@Class_System_OverflowException@@SIXPAU1@@Z
if SINGLE_THREADED
        dec ?c_allocationInhibitGC@Class_System_GC@@2_NA
else ; SINGLE_THREADED
        lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA
endif ; SINGLE_THREADED
        mov ecx,esi
        mov edx,ebx
        pop esi
        pop ebx
        jmp __throwDispatcherExplicitAddr
__throwOverflowException endp

ifdef SINGULARITY_KERNEL
;
;  void Thread.setStopContext(Thread t, Exception exn)
;

align 16
?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z proc
        ; ecx = ecx.context
        add ecx, Class_System_Threading_Thread._context
        ; context.eip = __throwBeyondMarker
        ; context.ecx = processStopException
        ; context.eax = context.stackMarkers
        mov     [ecx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ip, __throwBeyondMarker
        mov     [ecx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._cx, edx
        mov     eax, [ecx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers
        mov     [ecx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ax, eax
        ret
?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z endp

endif ; SINGULARITY_KERNEL

;
;  int System.VTable.doubleToInt(double)
;

align 16
?g_doubleToInt@Class_System_VTable@@SIHN@Z proc
        push        ebp
        mov         ebp,esp
        add         esp,-8
        fld         real8 ptr [ebp+8]
        wait
        fnstcw      word ptr [ebp-2]
        wait
        mov         ax,word ptr [ebp-2]
        or          eax,0C00h
        mov         word ptr [ebp-4],ax
        fldcw       word ptr [ebp-4]
        fistp       dword ptr [ebp-8]
        fldcw       word ptr [ebp-2]
        mov         eax,dword ptr [ebp-8]

        cmp         eax,080000000h
        je          possible_overflow
return:
        mov         esp,ebp
        pop         ebp
        ret         8

possible_overflow:
        fld         real8 ptr [ebp+8]
        fcomp       real8 ptr $DBLMAXINT
        fnstsw      ax
        test        ah,4
        jne         short return_zero
        test        ah,1
        jne         short return_MININT

return_MAXINT:
        mov         eax, 07fffffffh
        jmp         short return

return_zero:
        xor         eax, eax
        jmp         short return

return_MININT:
        mov         eax, 080000000h
        jmp         short return

?g_doubleToInt@Class_System_VTable@@SIHN@Z endp

;
;  long System.VTable.doubleToLong(double)
;

align 16
?g_doubleToLong@Class_System_VTable@@SI_JN@Z proc
        push        ebp
        mov         ebp,esp
        add         esp,-12
        fld         real8 ptr [ebp+8]
        wait
        fnstcw      word ptr [ebp-2]
        wait
        mov         ax,word ptr [ebp-2]
        or          eax,0C00h
        mov         word ptr [ebp-4],ax
        fldcw       word ptr [ebp-4]
        fistp       qword ptr [ebp-12]
        fldcw       word ptr [ebp-2]
        mov         eax,dword ptr [ebp-12]
        mov         edx,dword ptr [ebp-8]

        cmp         edx,080000000h
        je          possible_overflow
return:
        mov         esp,ebp
        pop         ebp
        ret         8

possible_overflow:
        mov         edx,eax                    ; save lsw
        fld         real8 ptr [ebp+8]
        fild        qword ptr $MAXLONG
        fcompp
        fnstsw      ax
        test        ah,4
        jne         short return_zero
        test        ah,65
        je          short check_MINLONG

return_MAXLONG:
        mov         eax, 0ffffffffh
        mov         edx, 07fffffffh
        jmp         short return

return_zero:
        xor         eax, eax
        xor         edx, edx
        jmp         short return

check_MINLONG:
        fld         real8 ptr [ebp+8]
        fild        qword ptr $MINLONG
        fcompp
        fnstsw      ax
        test        ah,1
        jne         short return_original

return_MINLONG:
        xor         edx, edx                   ; zero lsw

return_original:
        mov         eax, edx                   ; restore lsw to eax
        mov         edx, 080000000h
        jmp         short return

?g_doubleToLong@Class_System_VTable@@SI_JN@Z endp

;
;  int System.VTable.floatToInt(float)
;

align 16
?g_floatToInt@Class_System_VTable@@SIHM@Z proc
        push        ebp
        mov         ebp,esp
        add         esp,-8
        fld         real4 ptr [ebp+8]
        wait
        fnstcw      word ptr [ebp-2]
        wait
        xor         eax,eax
        mov         ax,word ptr [ebp-2]
        or          eax,0C00h
        mov         word ptr [ebp-4],ax
        fldcw       word ptr [ebp-4]
        fistp       dword ptr [ebp-8]
        fldcw       word ptr [ebp-2]
        mov         eax,dword ptr [ebp-8]

        cmp         eax,080000000h
        je          possible_overflow
return:
        mov         esp,ebp
        pop         ebp
        ret         4

possible_overflow:
        fld         real4 ptr [ebp+8]
        fcomp       real8 ptr $DBLMAXINT
        fnstsw      ax
        test        ah,4
        jne         short return_zero
        test        ah,1
        jne         short return_MININT
        mov         eax, 07fffffffh
        jmp         short return

return_zero:        
        xor         eax, eax
        jmp         short return

return_MININT:
        mov         eax, 080000000h
        jmp         short return

?g_floatToInt@Class_System_VTable@@SIHM@Z endp

;
;  long System.VTable.floatToLong(float)
;

align 16
?g_floatToLong@Class_System_VTable@@SI_JM@Z proc
        push        ebp
        mov         ebp,esp
        add         esp,-12
        fld         real4 ptr [ebp+8]
        wait
        fnstcw      word ptr [ebp-2]
        wait
        mov         ax,word ptr [ebp-2]
        or          eax,0C00h
        mov         word ptr [ebp-4],ax
        fldcw       word ptr [ebp-4]
        fistp       qword ptr [ebp-12]
        fldcw       word ptr [ebp-2]
        mov         eax,dword ptr [ebp-12]
        mov         edx,dword ptr [ebp-8]

        cmp         edx,080000000h
        je          possible_overflow
return:
        mov         esp,ebp
        pop         ebp
        ret         4

possible_overflow:
        mov         edx,eax                    ; save lsw
        fld         real4 ptr [ebp+8]
        fild        qword ptr $MAXLONG
        fcompp
        fnstsw      ax
        test        ah,4                                        
        jne         short return_zero
        test        ah,65
        je          short check_MINLONG

return_MAXLONG:
        mov         eax, 0ffffffffh
        mov         edx, 07fffffffh
        jmp         short return

return_zero:
        xor         eax, eax
        xor         edx, edx
        jmp         short return

check_MINLONG:
        fld         real4 ptr [ebp+8]
        fild        qword ptr $MINLONG
        fcompp
        fnstsw      ax
        test        ah,1
        jne         short return_original

return_MINLONG:
        xor         edx, edx                   ; zero lsw

return_original:
        mov         eax, edx                   ; restore lsw to eax
        mov         edx, 080000000h
        jmp         short return

?g_floatToLong@Class_System_VTable@@SI_JM@Z endp

;
;  int System.VTable.checkedDoubleToInt(double)
;

align 16
?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z proc
        push        ebp
        mov         ebp,esp
        add         esp,-8
        fld         real8 ptr [ebp+8]
        wait
        fnstcw      word ptr [ebp-2]
        wait
        mov         ax,word ptr [ebp-2]
        or          eax,0C00h
        mov         word ptr [ebp-4],ax
        fldcw       word ptr [ebp-4]
        fistp       dword ptr [ebp-8]
        fldcw       word ptr [ebp-2]
        mov         eax,dword ptr [ebp-8]

        cmp         eax,080000000h
        je          possible_overflow
return:
        mov         esp,ebp
        pop         ebp
        ret         8

possible_overflow:
        fld         real8 ptr [ebp+8]
        fcomp       real8 ptr $DBLMAXINT
        fnstsw      ax
        test        ah,4                    ; test for unordered
        jne         short throw_exception
        test        ah,1                    ; test for <$DBLMAXINT
        jne         short return_MININT
        ; src > $DBLMAXINT
        ; throw an overflow exception
        jmp         short throw_exception
        
return_MININT:
        ; check against $DBLMININT
        fld         real8 ptr [ebp+8]
        fcomp       real8 ptr $DBLMININT
        fnstsw      ax
        test        ah, 1                       ; test for < $DBLMININT
        jne         short throw_exception ; throw exception if true 
        mov         eax, 080000000h
        jmp         short return
        
throw_exception:
        ; throw an overflow exception
        ; set up stack frame so that it looks like a call to throwNewOverflowException
        ; from the caller of this function.
        mov         esp,ebp
        pop         ebp
        pop         eax       ; grab return address
        add         esp, 4    ; move esp pass the first parameter.
        mov         [esp],eax ; overwrite argument
        jmp         ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ    

?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z endp

;
;  long System.VTable.checkedDoubleToLong(double)
;

align 16
?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z proc
        push        ebp
        mov         ebp,esp
        add         esp,-12
        fld         real8 ptr [ebp+8]
        wait
        fnstcw      word ptr [ebp-2]
        wait
        mov         ax,word ptr [ebp-2]
        or          eax,0C00h
        mov         word ptr [ebp-4],ax
        fldcw       word ptr [ebp-4]
        fistp       qword ptr [ebp-12]
        fldcw       word ptr [ebp-2]
        mov         eax,dword ptr [ebp-12]
        mov         edx,dword ptr [ebp-8]

        cmp         edx,080000000h
        je          possible_overflow
return:
        mov         esp,ebp
        pop         ebp
        ret         8

possible_overflow:
        mov         edx,eax                    ; save lsw
        fld         real8 ptr [ebp+8]
        fild        qword ptr $MAXLONG
        fcompp
        fnstsw      ax
        test        ah,4                       ; test for unordered
        jne         short return_zero
        test        ah,65                      ; test for <= $MAXLONG
        je          short check_MINLONG

return_MAXLONG:
        ; src > $MAXLONG
        ; throw an exception
        jmp         short throw_exception

return_zero:
        jmp         short throw_exception

check_MINLONG:
        ; src <= $MINLONG
        fild        qword ptr $MINLONG
        fld         real8 ptr [ebp+8]
        fcompp                       ; real8 ptr [ebp+8] < $MINLONG
        fnstsw      ax
        test        ah,1
        jne         short throw_exception
        
return_MINLONG:
        mov         eax, edx                   ; restore lsw to eax
        mov         edx, 080000000h
        jmp         short return
        

throw_exception:
        ; throw an overflow exception
        ; set up stack frame so that it looks like a call to throwNewOverflowException
        ; from the caller of this function.
        mov         esp,ebp
        pop         ebp
        pop         eax       ; grab return address
        add         esp, 4    ; move esp pass the first parameter.
        mov         [esp],eax ; overwrite argument
        jmp         ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ
       
?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z endp

;
;  int System.VTable.checkedFloatToInt(float)
;

align 16
?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z proc
        push        ebp
        mov         ebp,esp
        add         esp,-8
        fld         real4 ptr [ebp+8]
        wait
        fnstcw      word ptr [ebp-2]
        wait
        xor         eax,eax
        mov         ax,word ptr [ebp-2]
        or          eax,0C00h
        mov         word ptr [ebp-4],ax
        fldcw       word ptr [ebp-4]
        fistp       dword ptr [ebp-8]
        fldcw       word ptr [ebp-2]
        mov         eax,dword ptr [ebp-8]

        cmp         eax,080000000h
        je          possible_overflow

return:
        mov         esp,ebp
        pop         ebp
        ret         4

possible_overflow:
        fld         real4 ptr [ebp+8]
        fcomp       real8 ptr $DBLMAXINT
        fnstsw      ax
        test        ah,4                        ; test for unordered
        jne         short throw_exception
        test        ah,1                        ; test for src < $DBLMAXINT
        jne         short return_MININT
        ; src > $DBLMAXINT
        ; throw an overflow exception
        jmp         short throw_exception

return_MININT:
        ; need to check against $DBLMININT, if it is less than,
        ; then throw an overflow exception
        fld         real4 ptr [ebp+8]
        fcomp       real8 ptr $DBLMININT
        fnstsw      ax
        test        ah,1                        ; test for less than
        jne         short throw_exception       
        mov         eax, 080000000h
        jmp         short return

throw_exception:
        ; throw an overflow exception
        ; set up stack frame so that it looks like a call to throwNewOverflowException
        ; from the caller of this function.
        mov         esp,ebp
        pop         ebp
        pop         eax       ; grab return address
        mov         [esp],eax ; overwrite argument
        jmp         ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ

?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z endp

;
;  long System.VTable.checkedFloatToLong(float)
;

align 16
?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z proc
        push        ebp
        mov         ebp,esp
        add         esp,-12
        fld         real4 ptr [ebp+8]
        wait
        fnstcw      word ptr [ebp-2]
        wait
        mov         ax,word ptr [ebp-2]
        or          eax,0C00h
        mov         word ptr [ebp-4],ax
        fldcw       word ptr [ebp-4]
        fistp       qword ptr [ebp-12]
        fldcw       word ptr [ebp-2]
        mov         eax,dword ptr [ebp-12]
        mov         edx,dword ptr [ebp-8]

        cmp         edx,080000000h
        je          possible_overflow
return:
        mov         esp,ebp
        pop         ebp
        ret         4

possible_overflow:
        mov         edx,eax                    ; save lsw
        fld         real4 ptr [ebp+8]
        fild        qword ptr $MAXLONG
        fcompp
        fnstsw      ax
        test        ah,4                        ; test for unordered
        jne         short return_zero
        test        ah,65                       ; test for <= $MAXLONG
        je          short check_MINLONG

return_MAXLONG:
        ; src > $MAXLONG
        ; throw an exception
        jmp         short throw_exception

return_zero:
        ; compare with $MAXLONG results in unordered
        ; throw an overflow exception
        jmp         short throw_exception

check_MINLONG:
        ; src <= $MINLONG
        fild        qword ptr $MINLONG
        fld         real4 ptr [ebp+8]
        fcompp                                  ; real8 ptr [ebp+8] < $MINLONG
        fnstsw      ax
        test        ah,1                        
        jne         short throw_exception ;  throw an overflow exception when src < $MINLONG  
return_MINLONG:
        mov         eax, edx        ;  restore lsw
        mov         edx, 080000000h
        jmp         short return

throw_exception:
        ; throw an overflow exception
        ; set up stack frame so that it looks like a call to throwNewOverflowException
        ; from the caller of this function.
        mov         esp,ebp
        pop         ebp
        pop         eax       ; grab return address
        mov         [esp],eax ; overwrite argument
        jmp         ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ

?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z endp

align 16
?g_floatRem@Class_System_VTable@@SIMMM@Z proc                                
        fld     real4 ptr [esp+8]
        fld     real4 ptr [esp+4]
fremloop:        
        fprem
        fstsw   ax        
        fwait
        sahf
        jp      fremloop    ; Continue while the FPU status bit C2 is set
        ffree st(1)
        ret     8
?g_floatRem@Class_System_VTable@@SIMMM@Z endp

align 16
?g_doubleRem@Class_System_VTable@@SINNN@Z proc
        fld     real8 ptr [esp+12]
        fld     real8 ptr [esp+4]
fremloop:
        fprem
        fstsw   ax        
        fwait
        sahf
        jp      fremloop    ; Continue while the FPU status bit C2 is set
        ffree st(1)
        ret     16
?g_doubleRem@Class_System_VTable@@SINNN@Z endp


if EXCLUDED        
;
;  void __checkFPStackDepth0
;

align 16
__checkFPStackDepth0 proc
        push        ebp
        mov         ebp,esp

        push        eax
        pushfd
 
        xor         eax,eax

        wait
        fnstsw      ax
        wait

        shr         eax,11
        and         eax,7
        cmp         eax,0
        je          ok
oops:
        int         3
ok:

        popfd
        pop         eax

        pop         ebp
        ret         0
__checkFPStackDepth0 endp

;
;  void __checkFPStackDepth1
;

align 16
__checkFPStackDepth1 proc
        push        ebp
        mov         ebp,esp

        push        eax
        pushfd
 
        xor         eax,eax

        wait
        fnstsw      ax
        wait

        shr         eax,11
        and         eax,7
        cmp         eax,8-1
        je          ok
oops:
        int         3
ok:

        popfd
        pop         eax

        pop         ebp
        ret         0
__checkFPStackDepth1 endp

;
;  void __checkFPStackDepth2
;

align 16
__checkFPStackDepth2 proc
        push        ebp
        mov         ebp,esp

        push        eax
        pushfd
 
        xor         eax,eax

        wait
        fnstsw      ax
        wait

        shr         eax,11
        and         eax,7
        cmp         eax,8-2
        je          ok
oops:
        int         3
ok:

        popfd
        pop         eax

        pop         ebp
        ret         0
__checkFPStackDepth2 endp

;
;  void __checkFPStackDepth3
;

align 16
__checkFPStackDepth3 proc
        push        ebp
        mov         ebp,esp

        push        eax
        pushfd
 
        xor         eax,eax

        wait
        fnstsw      ax
        wait

        shr         eax,11
        and         eax,7
        cmp         eax,8-3
        je          ok
oops:
        int         3
ok:

        popfd
        pop         eax

        pop         ebp
        ret         0
__checkFPStackDepth3 endp

;
;  void __checkFPStackDepth4
;

align 16
__checkFPStackDepth4 proc
        push        ebp
        mov         ebp,esp

        push        eax
        pushfd
 
        xor         eax,eax

        wait
        fnstsw      ax
        wait

        shr         eax,11
        and         eax,7
        cmp         eax,8-4
        je          ok
oops:
        int         3
ok:

        popfd
        pop         eax

        pop         ebp
        ret         0
__checkFPStackDepth4 endp

;
;  void __checkFPStackDepth5
;

align 16
__checkFPStackDepth5 proc
        push        ebp
        mov         ebp,esp

        push        eax
        pushfd
 
        xor         eax,eax

        wait
        fnstsw      ax
        wait

        shr         eax,11
        and         eax,7
        cmp         eax,8-5
        je          ok
oops:
        int         3
ok:

        popfd
        pop         eax

        pop         ebp
        ret         0
__checkFPStackDepth5 endp

;
;  void __checkFPStackDepth6
;

align 16
__checkFPStackDepth6 proc
        push        ebp
        mov         ebp,esp

        push        eax
        pushfd
 
        xor         eax,eax

        wait
        fnstsw      ax
        wait

        shr         eax,11
        and         eax,7
        cmp         eax,8-6
        je          ok
oops:
        int         3
ok:

        popfd
        pop         eax

        pop         ebp
        ret         0
__checkFPStackDepth6 endp

;
;  void __checkFPStackDepth7
;

align 16
__checkFPStackDepth7 proc
        push        ebp
        mov         ebp,esp

        push        eax
        pushfd
 
        xor         eax,eax

        wait
        fnstsw      ax
        wait

        shr         eax,11
        and         eax,7
        cmp         eax,8-7
        je          ok
oops:
        int         3
ok:

        popfd
        pop         eax

        pop         ebp
        ret         0
__checkFPStackDepth7 endp
endif ; EXCLUDED

;;; 
;;; 
;;; 
;;; 

ifdef SINGULARITY            
?g_ZeroPages@Class_System_Buffer@@SIXPAEH@Z proc
        ;; ECX = dst
        ;; EDX = len (bytes)
        
        push    ebp
        mov     ebp, esp
        pxor    mm0, mm0
next:
        movntq  [ecx + 0], mm0
        movntq  [ecx + 8], mm0
        movntq  [ecx + 16], mm0
        movntq  [ecx + 24], mm0
        movntq  [ecx + 32], mm0
        movntq  [ecx + 40], mm0
        movntq  [ecx + 48], mm0
        movntq  [ecx + 56], mm0
        add     ecx, 64
        sub     edx, 64
        ja      next
        
        sfence
        emms
        pop     ebp
        ret
?g_ZeroPages@Class_System_Buffer@@SIXPAEH@Z endp

?g_CopyPages@Class_System_Buffer@@SIXPAE0H@Z proc
        ;; ECX = dst
        ;; EDX = src
        ;; [ebp+8]  len (bytes)
        
        push    ebp
        mov     ebp, esp
        mov     eax, [ebp + 8]

        cmp     ecx, edx
        js      down

        ;; destination is lower than source
        add     ecx, eax
        add     edx, eax
        sub     ecx, 64
        sub     edx, 64
         
up:
        movq    mm0, [edx +  0]
        movq    mm1, [edx +  8]
        movq    mm2, [edx + 16]
        movq    mm3, [edx + 24]
        movq    mm4, [edx + 32]
        movq    mm5, [edx + 40]
        movq    mm6, [edx + 48]
        movq    mm7, [edx + 56]
        movntq  [ecx +  0], mm0
        movntq  [ecx +  8], mm1
        movntq  [ecx + 16], mm2
        movntq  [ecx + 24], mm3
        movntq  [ecx + 32], mm4
        movntq  [ecx + 40], mm5
        movntq  [ecx + 48], mm6
        movntq  [ecx + 56], mm7
        sub     ecx, 64
        sub     edx, 64
        sub     eax, 64
        ja      up

        sfence
        emms
        pop     ebp
        ret     4
        
        ;; destination is higher than source
down:
        movq    mm0, [edx +  0]
        movq    mm1, [edx +  8]
        movq    mm2, [edx + 16]
        movq    mm3, [edx + 24]
        movq    mm4, [edx + 32]
        movq    mm5, [edx + 40]
        movq    mm6, [edx + 48]
        movq    mm7, [edx + 56]
        movntq  [ecx +  0], mm0
        movntq  [ecx +  8], mm1
        movntq  [ecx + 16], mm2
        movntq  [ecx + 24], mm3
        movntq  [ecx + 32], mm4
        movntq  [ecx + 40], mm5
        movntq  [ecx + 48], mm6
        movntq  [ecx + 56], mm7
        add     ecx, 64
        add     edx, 64
        sub     eax, 64
        ja      down

        sfence
        emms
        pop     ebp
        ret     4
?g_CopyPages@Class_System_Buffer@@SIXPAE0H@Z endp
        
endif

end
