            include aoalib.inc
            include aoaExcepts.inc


            .code
            
; genericLSH is a generic language-specific 
; handler that gets called by a procedure-specific
; handler. This procedure has the following
; parameters:
;
;  RCX:     pExceptionRecord (Windows supplies this)
;  RDX:     pEstablisherFrame (Windows supplies this)
;  R8:      pDispatcherContext (Windows supplies this)
;  R9:      pDispatcherContext (Windows supplies this)
;  R10:     pScopeTable (procedure-specific handler
;                        supplies this).

rcxER       textequ <(ExceptionRecord_t ptr [rcx])>
rcxDC       textequ <(DispatcherContext ptr [rcx])>
r8SC        textequ <(scopeTable ptr [r8])>

genericLSH  proc    pExceptionRecord:  qword,  \
                    pEstablisherFrame: qword,  \
                    pContextRecord:    qword,  \
                    pDispatcherContext:qword,  \
                    pScopeTable:       qword
                    
; Note: 1 qword + 2 dwords leave stack 16-byte aligned.

            local   scPtr:             qword
            local   count[2]:          dword
            
            
; shadowStorage must always be last so it is on the
; bottom of the stack:

            local   shadowStorage[64]:byte
            
            
; Try and use volatile registers throughout this
; procedure so we don't have to deal with saving
; and restoring them. Begin by saving the volatile
; (parameter) register values into the shadow locations
; on the stack.

            mov     pExceptionRecord, rcx
            mov     pEstablisherFrame, rdx
            mov     pContextRecord, r8
            mov     pDispatcherContext, r9
            mov     pScopeTable, r10

; If the exception is not continuable, quit the program.

            cmp     rcxER.ExceptionFlags, EXCEPTION_NONCONTINUABLE
            jne     isCont
            
; If it's non-continuable (why are we even here?), 
; then quit the process

ExitPgm:    mov     rcx, 1
            call    ExitProcess

; Bail if this is an unwind operation; this 
; procedure-specific handler doesn't deal with that.

isCont:     mov     eax, ExceptionContinueSearch
            test    dword ptr rcxER.ExceptionFlags, \
                            EXCEPTION_UNWIND ;66h
            
            jnz     exitLSH         
            


; Set up for the search loop. On entry to this procedure,
; pScopeTable contained a pointer to a ScopeTable
; record that has the following form:
;
;  count:dword
;
;  scArray scopeTable count dup ({})
;
; (scArray is a variable-sized array with 1 or more
;  scopeTable records in it).
;
; Each scopeTable entry as the following:
;
; scopeTable        struct
;    exType         dword    2 dup (?)
;    beginAddress   qword    ?
;    endAddress     qword    ?
;    jumpTarget     qword    ?
;    finallyCode    qword    ?
; scopeTable        ends
 
execExceptHndlr:
            mov     rcx, pExceptionRecord
            mov     edx, rcxER.ExceptionCode
            mov     rcx, pDispatcherContext
            mov     rcx, rcxDC.ControlPc
            mov     r8, pScopeTable
            mov     eax, [r8]       ;Get element count
            add     r8, 4           ;Point at 1st element
            mov     scPtr, r8
            mov     count, eax

; For each element in the array of scopeTable records,
; see if the interrupted RIP address (held in RCX)
; is less than the beginAddress field, greater or
; equal to the endAddress field, or that the
; exception code (in edx) doesn't match the value
; in the exType field. If any of these conditions
; exist, move on to the next element of the scopeTable
; array.

searchScps:
                    
; See if we're within the range of the protected
; try block:

            cmp     rcx, [r8].scopeTable.beginAddress
            jb      cantHandle
            
            cmp     rcx, [r8].scopeTable.endAddress
            jae     cantHandle

; See if the exception type matches:

            cmp     [r8].scopeTable.exType, 0
            je      HandleException
            cmp     edx, [r8].scopeTable.exType
            je      HandleException
                    
cantHandle: add     r8, sizeof scopeTable
            dec     count
            jnz     searchScps
            
; If we ran out of entries in the scopeTable array:

            mov     eax, ExceptionContinueSearch
            jmp     exitLSH  

; Call RtlUnwind to unwind the stack and transfer
; control to the actual exception handler code.
;
; RtlUnwind has the following parameters:
;
; RCX: target frame (pEstablisherFrame passed to this code)
; RDX: Address of the handler code (found at
;           r8SC.jumpTarget)
; R8:  pExceptionRecord (passed to this code)
; R9:  Return value that is passed on to the
;      exception handler in RAX.

HandleException:
            mov     rdx, r8SC.jumpTarget
            mov     r8, pExceptionRecord
            mov     rcx, pEstablisherFrame
            
; This code is going to pass the pointer to
; the exception record to the handler, as that's
; probably the most useful piece of information
; it can have. Remember, this value shows
; up in RAX in the actual exception handler.

            mov     r9, r8
            call    RtlUnwind
            
; The code should never return from RtlUnwindEx
; unless there was an error. In that case,
; abort the program.

            mov     rcx, 1
            call    ExitProcess 

; Exit here if this procedure can't handle
; the exception.    
            
exitLSH::
            ret     
genericLSH  endp 
            end     