;***
;exsup3.asm
;
;	Copyright (c) 1994 Microsoft Corporation. All rights reserved.
;
;Purpose:
;	Exception handling for i386.  This is just the C9.0 version of
;	the language-specific exception handler.  The C8.0 version is
;	found in exsup2.asm, and the routines common to both C8 and C9
;	are found in exsup.asm.
;
;Notes:
;
;Revision History:
;	01-10-94  PML	Create VC/C++ 2.0 (C9.0) version from C8.0 original
;
;*******************************************************************************

;hnt = -D_WIN32_ -Dsmall32 -Dflat32 -Mx $this;

;Define small32 and flat32 since these are not defined in the NT build process
small32 equ 1
flat32  equ 1

ifdef _WIN32_
_WIN32_OR_POSIX_ equ 1
endif
ifdef _POSIX_
_WIN32_OR_POSIX_ equ 1
endif

.xlist
include pversion.inc
?DFDATA =	1
?NODATA =	1
include cmacros.mas
include exsup.inc
.list

;REVIEW: can we get rid of _global_unwind2, and just use
; the C runtimes version, _global_unwind?

extrn __global_unwind2:near
extrn __local_unwind2:near

;typedef struct _EXCEPTION_REGISTRATION PEXCEPTION_REGISTRATION;
;struct _EXCEPTION_REGISTRATION{
;/* _esp, xpointers at negative offset */
;     int _esp;
;     PEXCEPTION_POINTERS xpointers;
;     struct _EXCEPTION_REGISTRATION *prev;
;     void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION, PCONTEXT, PEXCEPTION_RECORD);
;     struct scopetable_entry *scopetable;
;     int trylevel;
;};
;private (at negative offsets from node ptr)
    _esp		=	-8
    xpointers		=	-4
_C9_EXCEPTION_REGISTRATION struc	; C9.0 version
;public:
                        dd      ?	; prev (common)
                        dd      ?	; handler (common)
;private:
    scopetable          dd      ?
    trylevel            dd      ?
_C9_EXCEPTION_REGISTRATION ends
FRAME_EBP_OFFSET equ 16

;#define EXCEPTION_MAXIMUM_PARAMETERS 4
;typedef struct _EXCEPTION_RECORD EXCEPTION_RECORD;
;typedef EXCEPTION_RECORD *PEXCEPTION_RECORD;
;struct _EXCEPTION_RECORD{
;    NTSTATUS ExceptionCode;
;    ULONG ExceptionFlags;
;    struct _EXCEPTION_RECORD *ExceptionRecord;
;    PVOID ExceptionAddress;
;    ULONG NumberParameters;
;    ULONG ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
;};
_EXCEPTION_RECORD struc
    exception_number    dd      ?
    exception_flags     dd      ?
    exception_record    dd      ?
    exception_address   dd      ?
    number_parameters   dd      ?
    exception_information dd 4 dup(?)
_EXCEPTION_RECORD ends
SIZEOF_EXCEPTION_RECORD equ 36

;/* following is the structure returned by the _exception_info() intrinsic. */
;typedef struct _EXCEPTION_POINTERS EXCEPTION_POINTERS;
;typedef struct EXCEPTION_POINTERS *PEXCEPTION_POINTERS;
;struct _EXCEPTION_POINTERS{
;    PEXCEPTION_RECORD ExceptionRecord;
;    PCONTEXT Context;
;};
_EXCEPTION_POINTERS struc
    ep_xrecord          dd      ?
    ep_context          dd      ?
_EXCEPTION_POINTERS ends
SIZEOF_EXCEPTION_POINTERS equ 8

assumes DS,DATA
assumes FS,DATA

__except_list equ 0

;struct _SCOPETABLE_ENTRY{
;     int enclosing_level;              /* lexical level of enclosing scope */
;     int (*filter)(PEXCEPTION_RECORD); /* NULL for a termination handler */
;     void (*specific_handler)(void);   /* xcpt or termination handler */
;};
;struct _SCOPETABLE_ENTRY Scopetable[NUMTRYS];
_SCOPETABLE_ENTRY struc
    enclosing_level     dd      ?
    filter              dd      ?
    specific_handler    dd      ?
_SCOPETABLE_ENTRY ends

BeginCODE
if	@Version LT 600
ifdef _WIN32_OR_POSIX_
;this needs to go here to work around a masm5 bug. (emits wrong fixup)
assumes CS,FLAT
else
assumes CS,CODE
endif
endif

;/* _EXCEPT_HANDLER3 - Try to find an exception handler listed in the scope
; * table associated with the given registration record, that wants to accept
; * the current exception. If we find one, run it (and never return).
; * RETURNS: (*if* it returns)
; *  DISPOSITION_DISMISS - dismiss the exception.
; *  DISPOSITION_CONTINUE_SEARCH - pass the exception up to enclosing handlers
; */
;int _except_handler3(
;       PEXCEPTION_RECORD exception_record,
;       PEXCEPTION_REGISTRATION registration,
;       PCONTEXT context,
;       PEXCEPTION_REGISTRATION dispatcher)
;{
;    int ix, filter_result;
;
;    for(ix=registration->trylevel; ix!=-1; ix=registration->xscope[ix].enclosing_level){
;       /* if filter==NULL, then this is an entry for a termination handler */
;       if(registration->xscope[ix].filter){
;           /* NB: call to the filter may trash the callee save
;              registers. (this is *not* a standard cdecl function) */
;           filter_result=(*registration->xscope[ix].filter)(xinfo);
;           if(filter_result==FILTER_DISMISS)
;               return(-1); /* dismiss */
;           if(filter_result==FILTER_ACCEPT){
;               _global_unwind2(registration);
;               _local_unwind2(registration, ix);
;               (*registration->xscope[ix].specific_handler)(void);
;               assert(UNREACHED); /*should never return from handler*/
;           }
;           assert(filter_result==FILTER_CONTINUE_SEARCH);
;       }
;    }
;    return(0); /* didnt find one */
;}
	db	'VC20'	;; VC/C++ 2.0/32-bit (C9.0) version
	db	'XC00'	;; so debugger can recognize this proc (cuda:3936)
cProc _except_handler3,<C,PUBLIC>,<IBX,ISI,IDI,IBP>
        parmDP  xrecord
        parmDP  registration
        parmDP  context
        parmDP  dispatcher
        localV  xp,SIZEOF_EXCEPTION_POINTERS
cBegin
	;4*4b for callee saves + 4b return address + 4b param = 24

	;DF in indeterminate state at time of exception, so clear it
	cld

        mov     ebx, registration               ;ebx= PEXCEPTION_REGISTRATION
        mov     eax, xrecord

        test    [eax.exception_flags], EXCEPTION_UNWIND_CONTEXT
        jnz     short _lh_unwinding

        ;build the EXCEPTION_POINTERS locally store its address in the
        ; registration record. this is the pointer that is returned by
        ; the _eception_info intrinsic.
        mov     xp.ep_xrecord, eax
        mov     eax, context
        mov     xp.ep_context, eax
        lea     eax, xp
        mov     [ebx.xpointers], eax

        mov     esi, [ebx.trylevel]             ;esi= try level
        mov     edi, [ebx.scopetable]           ;edi= scope table base
_lh_top:
        cmp     esi, -1
        je      short _lh_bagit
        lea     ecx, [esi+esi*2]                ;ecx= trylevel*3
        cmp     dword ptr [(edi+ecx*4).filter], 0
        je      short _lh_continue              ;term handler, so keep looking

        ;filter may trash *all* registers, so save ebp and scopetable offset
        push    esi
        push    ebp

	lea	ebp, FRAME_EBP_OFFSET[ebx]
        call    [(edi+ecx*4).filter]            ;call the filter

        pop     ebp
        pop     esi
        ;ebx may have been trashed by the filter, so we must reload
        mov     ebx, registration

	; Accept <0, 0, >0 instead of just -1, 0, +1
	or	eax, eax
	jz	short _lh_continue
	js	short _lh_dismiss
        ;assert(eax==FILTER_ACCEPT)

        ;reload xscope base, cuz it was trashed by the filter call
        mov     edi, [ebx.scopetable]
        ;load handler address before we loose components of address mode
        push    ebx                             ;registration*
        call    __global_unwind2                ;run term handlers
        add     esp, 4

        ;setup ebp for the local unwinder and the specific handler
	lea	ebp, FRAME_EBP_OFFSET[ebx]

        ;the stop try level == accepting except level
        push    esi                             ;stop try level
        push    ebx                             ;registration*
        call    __local_unwind2
        add     esp, 8

        lea     ecx, [esi+esi*2]                ;ecx=trylevel*3
; set the current trylevel to our enclosing level immediately
; before giving control to the handler. it is the enclosing
; level, if any, that guards the handler.
        mov     eax, [(edi+ecx*4).enclosing_level]
        mov     [ebx.trylevel], eax
        call    [(edi+ecx*4).specific_handler]  ;call the except handler
        ;assert(0)                              ;(NB! should not return)

_lh_continue:
        ;reload the scope table base, possibly trashed by call to filter
        mov     edi, [ebx.scopetable]
        lea     ecx, [esi+esi*2]
        mov     esi, [edi+ecx*4+0]              ;load the enclosing trylevel
        jmp     short _lh_top

_lh_dismiss:
        mov     eax, DISPOSITION_DISMISS        ;dismiss the exception
        jmp     short _lh_return

_lh_bagit:
        mov     eax, DISPOSITION_CONTINUE_SEARCH
        jmp     short _lh_return

_lh_unwinding:
        push    ebp
	lea	ebp, FRAME_EBP_OFFSET[ebx]
        push    -1
        push    ebx
        call    __local_unwind2
        add     esp, 8
        pop     ebp
        ;the return value is not really relevent in an unwind context
        mov     eax, DISPOSITION_CONTINUE_SEARCH

_lh_return:
cEnd

public __seh_longjmp_unwind@4
__seh_longjmp_unwind@4 proc near
	push	ebp
	mov	ecx, 8[esp]
	mov	ebp, [ecx.saved_ebp]
	mov	eax, [ecx.saved_trylevel]
	push	eax
	mov	eax, [ecx.saved_xregistration]
	push	eax
	call	__local_unwind2
	add	esp, 8
	pop	ebp
	ret	4
__seh_longjmp_unwind@4 endp

EndCODE
END
