; Listing7-4.asm
;
; A program that demonstrates handling
; an exception in pure (no macros)
; assembly language.


            option     casemap:none
            .nolist
            include    aoalib.inc
            include    aoaExcepts.inc
            includelib aoalib.lib
            .list
            
            .const

; Program title:

             align  word
ttlStr       byte   "Listing 7-4", 0


            .code

            

; The following is a procedure-specific handler
; for the tstSEH procedure.

procSpec    proc
            lea     r10, tstSEH$scopeTable
            jmp     genericLSH
procSpec    endp


; tstSEH-
;
; This procedure tests exception handling code in
; assembly language.

            option  prologue:none
            option  epilogue:none            
tstSEH      proc    frame:procSpec
            
            local   endTryPtr:qword 
            local   shadowStorage[48]:byte
            
            push    rbp
            mov     rbp, rsp
            push    rax
            .allocstack 8
            push    rbx
            .pushreg rbx
            push    rcx
            .allocstack 8
            sub     rsp, 56
            .allocstack 56
            .setframe rbp, 80
            .endprolog      
            
; Begin the protected block:
;
; try
     
startTry::
            call    print
            byte    "startTry: int 3 is next instr", nl, 0
            
            int     3       ;Cause an exception
            
            call    print
            byte    "This should not print", nl, 0
            
; This will never execute, but inlucde the code just
; to show the structure of the try..except..endtry
; code.
            call    finallyCode     ;Execute finally block
            jmp     endTryCode
endProtected:



; Generate the scopeTable entry for the 
; following breakpoint except block:

            option  dotname
.xdata      segment read align(8)
tstSEH$scopeTable dword 1
            dword   ex(brkpt)
            dword   0
            qword   startTry
            qword   endProtected
            qword   brkPtExc
            qword   finallyCode
.xdata      ends

; Here's the code that executes whenever a memory
; access violation occurs in the protected block.
;
; Note that the call to the finallyCode can happen
; before or after the body of the exception handler.
; Normally, it's best if it happens first (to
; guarantee execution).

; exception( ex(brkpt) )


brkPtExc:   push    rax     ;Preserve RAX across call
            push    rax     ;Keep 16-byte alignment
            call    finallyCode
            pop     rax
            pop     rax
            
; Upon entry into this code, RAX contains a pointer
; to the DispatcherContext record. Print a couple
; of useful values from that record:

            mov     edx, [rax].ExceptionRecord_t.ExceptionCode
            call    print
            byte    "Breakpoint encountered (0x%x)", nl, 0
            jmp     endTryCode
            
; Here's the finally code; This corresponds to the
; 

finallyCode:
            call    print
            byte    "Executed finally code", nl, 0
            ret     0       ;Must be RET 0!

            

; This is where the code exits the protected loop.
; 
; endtry
           
endTryCode:

; Generate the epilog code (including ret) and end
; the procedure:

            add     rsp, 56
            pop     rcx
            pop     rbx
            pop     rax
            leave
            ret     0
tstSEH      endp


        
;--------------------------------------------------
;
; Here is the main assembly language function.

            
            public  asmMain
            
; For the main assembly procedure, preserve all the 
; non-volatile registers. It would be cool to save  
; the XMM non-volatile registers, too, but there isn't 
; room in the prolog for them.
; 
; Had this code used a biased frame pointer (RBP) value, 
; it could have gotten away with saving them. Epilog 
; and prolog  macros don't allow that, however. So this 
; code preserves what it can (the general-purpose 64-bit 
; non-volatile registers).
            
asmMain     proc    frame
            local   shadowStorage[56]:byte
            
            push    rbp
            mov     rbp, rsp
            push    rbx
            .pushreg rbx
            push    rsi
            .pushreg rsi
            push    rdi
            .pushreg rdi
            push    r12
            .pushreg r12
            push    r13
            .pushreg r13
            push    r14
            .pushreg r14
            push    r15
            .pushreg r15
            sub     rsp, 56
            .allocstack 56
            .setframe rbp, 112
            .endprolog 

; Call the procedure the demonstrates exception handling:

            call    print
            byte    "Calling tstSEH", nl, 0
            
; Call the procedure that raises an exception:

            call    tstSEH
            
            call    print
            byte    "Back from tstSEH", nl, 0
            
            add     rsp, 56
            pop     r15
            pop     r14
            pop     r13
            pop     r12
            pop     rdi
            pop     rsi
            pop     rbx
            leave
            ret     0
asmMain     endp
            end
