; aoaExcepts.inc ; ; Macros to support prolog and epilog to automatically ; generate prologs and epilogs for SEH-compatible ; procedures. ifndef aoaExcepts_inc aoaExcepts_inc = 0 include aoaMacros.inc ; Windows API functions. ; Defined this way to maintain compatibility with MASM32 win64.h ; header file. pproc TYPEDEF ptr proc externdef __imp_RtlLookupFunctionEntry:pproc RtlLookupFunctionEntry equ <__imp_RtlLookupFunctionEntry> externdef __imp_RtlUnwind:pproc RtlUnwind equ <__imp_RtlUnwind> externdef __imp_RaiseException:pproc RaiseException equ <__imp_RaiseException> externdef __imp_ExitProcess:pproc ExitProcess equ <__imp_ExitProcess> ; AOA Library functions: externdef genericLSH:proc externdef genericSEHfltr:proc ; Windows SEH Constants: ExceptionContinueExecution = 0 ExceptionContinueSearch = 1 ExceptionNestedException = 2 ExceptionCollidedUnwind = 3 EXCEPTION_EXECUTE_HANDLER = 1 EXCEPTION_CONTINUE_SEARCH = 0 EXCEPTION_CONTINUE_EXECUTION= -1 EXCEPTION_NONCONTINUABLE = 1 ; Noncontinuable exception EXCEPTION_UNWINDING = 2 ; Unwind is in progress EXCEPTION_EXIT_UNWIND = 4 ; Exit unwind is in progress EXCEPTION_STACK_INVALID = 8 ; Stack out of limits or unaligned EXCEPTION_NESTED_CALL = 10h ; Nested exception handler call EXCEPTION_TARGET_UNWIND = 20h ; Target unwind in progress EXCEPTION_COLLIDED_UNWIND = 40h ; Collided exception handler call EXCEPTION_UNWIND = 66h ; Exception codes: STATUS_WAIT_0 = 00000000h STATUS_ABANDONED_WAIT_0 = 00000080h STATUS_USER_APC = 000000C0h STATUS_TIMEOUT = 00000102h STATUS_PENDING = 00000103h STATUS_DATATYPE_MISALIGNMENT= 80000002h STATUS_BREAKPOINT = 80000003h STATUS_SINGLE_STEP = 80000004h STATUS_ACCESS_VIOLATION = 0C0000005h STATUS_IN_PAGE_ERROR = 0C0000006h STATUS_NO_MEMORY = 0C0000017h STATUS_ILLEGAL_INSTRUCTION = 0C000001Dh STATUS_NONCONTINUABLE_EXCEPTION= 0C0000025h STATUS_INVALID_DISPOSITION = 0C0000026h STATUS_ARRAY_BOUNDS_EXCEEDED= 0C000008Ch STATUS_FLOAT_DENORMAL_OPERAND= 0C000008Dh STATUS_FLOAT_DIVIDE_BY_ZERO = 0C000008Eh STATUS_FLOAT_INEXACT_RESULT = 0C000008Fh STATUS_FLOAT_INVALID_OPERATION= 0C0000090h STATUS_FLOAT_OVERFLOW = 0C0000091h STATUS_FLOAT_STACK_CHECK = 0C0000092h STATUS_FLOAT_UNDERFLOW = 0C0000093h STATUS_INTEGER_DIVIDE_BY_ZERO= 0C0000094h STATUS_INTEGER_OVERFLOW = 0C0000095h STATUS_PRIVILEGED_INSTRUCTION= 0C0000096h STATUS_STACK_OVERFLOW = 0C00000FDh STATUS_CONTROL_C_EXIT = 0C000013Ah EXCEPTION_CONTINUABLE = 0 EXCEPTION_NONCONTINUABLE = 1h EXCEPTION_ACCESS_VIOLATION = STATUS_ACCESS_VIOLATION EXCEPTION_DATATYPE_MISALIGNMENT= STATUS_DATATYPE_MISALIGNMENT EXCEPTION_BREAKPOINT = STATUS_BREAKPOINT EXCEPTION_SINGLE_STEP = STATUS_SINGLE_STEP EXCEPTION_ARRAY_BOUNDS_EXCEEDED= STATUS_ARRAY_BOUNDS_EXCEEDED EXCEPTION_FLT_DENORMAL_OPERAND= STATUS_FLOAT_DENORMAL_OPERAND EXCEPTION_FLT_DIVIDE_BY_ZERO= STATUS_FLOAT_DIVIDE_BY_ZERO EXCEPTION_FLT_INEXACT_RESULT= STATUS_FLOAT_INEXACT_RESULT EXCEPTION_FLT_INVALID_OPERATION= STATUS_FLOAT_INVALID_OPERATION EXCEPTION_FLT_OVERFLOW = STATUS_FLOAT_OVERFLOW EXCEPTION_FLT_STACK_CHECK = STATUS_FLOAT_STACK_CHECK EXCEPTION_FLT_UNDERFLOW = STATUS_FLOAT_UNDERFLOW EXCEPTION_INT_DIVIDE_BY_ZERO= STATUS_INTEGER_DIVIDE_BY_ZERO EXCEPTION_INT_OVERFLOW = STATUS_INTEGER_OVERFLOW EXCEPTION_ILLEGAL_INSTRUCTION= STATUS_ILLEGAL_INSTRUCTION EXCEPTION_PRIV_INSTRUCTION = STATUS_PRIVILEGED_INSTRUCTION EXCEPTION_IN_PAGE_ERROR = STATUS_IN_PAGE_ERROR ; Put the constants in the exceptions (ex) namespace: ex macro func namespace (&func, , <0>) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) namespace (&func, , ) echo ***************************** echo Error:unexpected ex member % echo function=func echo ***************************** exitm <.err> endm ; Windows SEH DispatcherConstext structure: DispatcherContext struct ControlPc qword ? ImageBase qword ? FunctionEntry qword ? EstablisherFrame qword ? TargetIp qword ? ContextRecord qword ? LanguageHandler qword ? HandlerData qword ? HistoryTable qword ? ScopeIndex dword ? padding dword ? DispatcherContext ends ; Windows SEH ExceptionRecord structure. ; ; This should be named "ExceptionRecord". ; However, the MASM32 win64.inc header ; file uses that name for a different ; purpose (creating namespace pollution) ; so AoA library uses ExceptionRecord_t ; instead. exMaxParameters = 15 ExceptionRecord_t struct ExceptionCode dword ? ExceptionFlags dword ? pExceptionRecord qword ? ExceptionAddress qword ? NumberParameters dword ? ExceptionInformation qword exMaxParameters dup (?) ExceptionRecord_t ends ; Windows SEH Context structure (for x86-64) ; Must align to 16 bytes Context struct 16 ; Register parameter home addresses ; (for future extension) P1Home qword ? P2Home qword ? P3Home qword ? P4Home qword ? P5Home qword ? P6Home qword ? ; Control flags. ContextFlags dword ? MxCsr dword ? ; Segment Registers and processor flags SegCs word ? SegDs word ? SegEs word ? SegFs word ? SegGs word ? SegSs word ? EFlags dword ? ; Debug registers _Dr0 qword ? _Dr1 qword ? _Dr2 qword ? _Dr3 qword ? _Dr6 qword ? _Dr7 qword ? ; Integer registers. _Rax qword ? _Rcx qword ? _Rdx qword ? _Rbx qword ? _Rsp qword ? _Rbp qword ? _Rsi qword ? _Rdi qword ? _R8 qword ? _R9 qword ? _R10 qword ? _R11 qword ? _R12 qword ? _R13 qword ? _R14 qword ? _R15 qword ? ; Program counter. _Rip qword ? Header oword 2 dup(?) Legacy oword 8 dup(?) _Xmm0 oword ? _Xmm1 oword ? _Xmm2 oword ? _Xmm3 oword ? _Xmm4 oword ? _Xmm5 oword ? _Xmm6 oword ? _Xmm7 oword ? _Xmm8 oword ? _Xmm9 oword ? _Xmm10 oword ? _Xmm11 oword ? _Xmm12 oword ? _Xmm13 oword ? _Xmm14 oword ? _Xmm15 oword ? ; Vector registers. VectorRegister oword 26 dup(?) VectorControl qword ? ; Special debug control registers DebugControl qword ? LastBranchToRip qword ? LastBranchFromRip qword ? LastExceptionToRip qword ? LastExceptionFromRip qword ? Context ends ; scopeTable is the ; language-specific handler data format ; for the Unwind_info exData field. scopeTable struct exType dword 2 dup (?) beginAddress qword ? endAddress qword ? jumpTarget qword ? finallyCode qword ? scopeTable ends ; Windows SEH unwind data structure. ; Note that "1 dup (?)" arrays are ; dynamically sized. Unwind_info struct versFlags byte ? sizeOfProlog byte ? countOfCodes byte ? frameVal byte ? UnwindCodes word 1 dup (?) ExHandler dword ? ExData scopeTable 1 dup ({}) Unwind_info ends RUNTIME_FUNCTION struct BeginAddress dword ? EndAddress dword ? UnwindData dword ? RUNTIME_FUNCTION ends ;------------------------------------------------- ; ; buildScope- ; Constructs a single scopeTable entry corresponding ; to one try/endtry block in a procedure. The endxp ; macro writes all the scopeTable entries to the ; .xdata segement at the end of the procedure. ; ; Through the assembly of an xproc..endxp, this macro ; builds an array of entries to add to the scopeTable ; for the procedure. These entries have the names: ; ; buildScope$0, buildScope$1, ..., buildScope$n ; ; (when there are n+1 entries in the scope table). ; Later on, the sehReturn macro will generate an ; .xdata segment all all of these array elements ; output to it (in order). buildScope$cntr = 0 buildScope macro excpt, \ beginProt, \ endProt, \ handler, \ finallyCode local scElement scElement catstr ,%buildScope$cntr @CatStr(,%buildScope$cntr) textequ < scopeTable {{&excpt,0}, &beginProt, &endProt, &handler, &finallyCode}> buildScope$cntr = buildScope$cntr + 1 endm filterProc macro procName &procName&$filter proc lea r10, &procName&$scopeTable jmp genericLSH &procName&$filter endp exitm <&procName&$filter> endm ;------------------------------------------------- ; ; isXMMReg and is64BitReg are two function macros ; this code uses to see if some text object ; corresponds to one of the SSE (XMM) or 64-bit ; general-purpose registers. ; ; isXMMReg ; ; Returns 1 for non-volatile XMM registers, ; 2 for volatile XMM registers, and ; 0 for anything else. isXMMReg macro parm local sseRegType ; Note that commas will never appear in "parm" because ; they separate items in the string being processed by ; the FOR loop. sseRegType instr 1, , <&parm> if sseRegType ne 0 exitm <2> endif sseRegType instr 1, , <&fparm> if sseRegType ne 0 exitm <1> endif exitm <0> endm ;isXMMReg ; is64bitReg ; ; Returns 1 for non-volatile 64-bit ; general-purpose registers, 2 for ; volatile 64-bit general-purpose ; registers, and 0 for anything else. is64bitReg macro parm local gpRegType gpRegType instr 1, , <&parm> if gpRegType ne 0 exitm <2> endif gpRegType instr 1, , <&parm> if gpRegType ne 0 exitm <1> endif exitm <0> endm ;is64bitReg ;---------------------------------------------------- ; ; xproc- Builds a "proc" directive for procedures ; than handle exceptions. ; ; Syntax: ; ; procName xproc( LSHandler, , parameters...) ; ; proceName xproc( frame, , parameters...) buildScope$cntr = 0 ;Redundant, but just in case... xproc macro frameFunc, regs, parms:vararg local frameProc option PROLOGUE:sehProlog option EPILOGUE:EPILOGUEDEF ; Reset the global scopeTable counter to 0; it counts the ; number of scope table entries within a procedure. buildScope$cntr = 0 ifidni <&frameFunc>, frameProc textequ else frameProc textequ endif ifnb <&parms> exitm , &frameProc, &parms> endif exitm , &frameProc> endm endxp macro option PROLOGUE:PROLOGUEDEF option EPILOGUE:EPILOGUEDEF sehReturn sehReturn textequ exitm endm ;------------------------------------------------------ ; ; sehProlog- ; ; The PROC directive invokes this macro when processing ; a procedure with the "frame" option. Note that ; "option prologue:sehProlog" must be active for MASM ; to automatically invoke this macro to generate the ; prolog code for the procedure. sehReturn textequ seh$procName textequ <> sehProlog macro procname, \ flag, \ parmbytes, \ localbytes, \ reglist, \ userparms local localCnt, reg64Cnt, xmmCnt local regOfs, rtnText, regVal ; Save the procedure name in a global so other macros ; can use it. Note that the sehProlog macro might not ; be invoked before the first "try" in a procedure, ; so you should not use this symbol in the try macro. seh$procName textequ <&procname> ; Standard entry stuff, push RBP and copy RSP into RBP: db 48h ;REX prefix for hot swapping. push rbp .pushreg rbp mov rbp, rsp ; Note: "uses" doesn't work when the "frame" option ; is present, so we're going to pass registers to ; preserve in the "userparms" text value. ; ; Begin by computing the amount of space we will ; need to store the registers on the stack (everything ; will be stored in local variables): reg64Cnt = 0 xmmCnt = 0 for reg, <&userparms> if is64bitReg( reg ) gt 0 reg64Cnt = reg64Cnt+1 else if isXMMReg( reg ) gt 0 xmmCnt = xmmCnt + 1 endif endif endm ; Compute the amount of space by which we have to ; drop RSP to make room for local variables. First, ; we have to round the register storage up to a ; multiple of 16 bytes (to guarantee that the locals ; are aligned to 16 bytes). Then ; we have to round ; the locals' size to the next multiple of 16 bytes. localCnt = (reg64Cnt*8 + xmmCnt*16 + 15) and -16 localCnt = ((localbytes + 15) and -16) + localCnt ; Drop the stack down by the specified amount to ; make room for the locals. if localCnt gt 0 sub rsp, localCnt .allocstack localCnt endif ; Now we can set the frame value, as we know how far ; up the stack RBP will be from RSP: .setframe rbp, localCnt ; Okay, store away all the XMM registers that ; were listed in the "userparms" entry of the ; PROC directive: regOfs = 0 for reg, <&userparms> regVal = isXMMReg( reg ) if regVal eq 1 regOfs = regOfs - 16 movdqa [rbp+regOfs], reg .savexmm128 reg, localCnt+regOfs elseif regVal eq 2 regOfs = regOfs - 16 movdqa [rbp+regOfs], reg endif endm ; Now, store away the 64-bit general-purpose registers ; that appear in the userparms list: for reg, <&userparms> regVal = is64bitReg( reg ) if regVal eq 1 regOfs = regOfs - 8 mov [rbp+regOfs], reg .savereg reg, localCnt+regOfs elseif regVal eq 2 regOfs = regOfs - 8 mov [rbp+regOfs], reg endif endm .endprolog ; Before returning, modify the "ret" text equate ; so that it invokes the epilog macro: sehReturn catstr , \ <&procname>, <, >, \ <&flag>, <, >, \ <&parmbytes>, <, >, \ <&localbytes>, <, >, \ <®list>, <, >, \ > ; The PROLOGUE macro must be a macro function that ; returns the total amount of space allocated on ; the stack (for pushed registers and locals), not ; including the RBP value. MASM uses this return ; result to assign offsets to the local variables. exitm %localCnt endm ;------------------------------------------------------------------ ; ; Here's the EPILOG macro that gets invoked to undo ; the work done by the PROLOG macro: sehEpilog macro procname, \ flag, \ parmbytes, \ localbytes, \ reglist, \ userparms local reg64Cnt, xmmCnt local regOfs, scName, scIndex ; Restore the XMM registers first: regOfs = 0 for reg, <&userparms> if isXMMReg( reg ) regOfs = regOfs - 16 movdqa reg, [rbp+regOfs] endif endm ; Now restore the 64-bit registers: for reg, <&userparms> if is64bitReg( reg ) regOfs = regOfs - 8 mov reg, [rbp+regOfs] endif endm ; Okay, pop the RBP value and return. leave ret 0 ; Okay, at the end of the procedure let's ; emit all the scopeTable entries. Do this ; exactly once per procedure so the Linker/loader ; doesn't rearrange the entries for a given ; procedure (they need to remain sorted by order ; of "endtry"). ; ; Note: the program refers to the (whole) scope ; table using the identifier $scopeTable if buildScope$cntr gt 0 option dotname scName catstr <&procname>,<$scopeTable> .xdata segment read align(8) scName dword buildScope$cntr scIndex = 0 while scIndex lt buildScope$cntr scName catstr ,%scIndex scName @CatStr(,%scIndex) textequ <> scIndex = scIndex+1 endm ;while .xdata ends endif ;buildScope$cntr gt 0 endm ;sehEpilog ;------------------------------------------------ ; ; Try macro- ; ; Handles the beginning of the "try" statement. ; ; Must do the following: ; ; 1) Push a unique "suffix" value onto the ; try stack. The unique suffix is generated ; by using the locally-generated name for ; the local symbol "suffix" (that is, something ; like "??xxxx"). ; ; 2) Emit a lable that marks the beginning of the ; try block's "protected" section. This must ; be a globally unique symbol. The program ; concatenates the suffix (above) to the string ; "try$start" to create this symbol. ; ; 3) This macro must also initialize various ; boolean values used by other macros in the ; try/exception/finally/endtry set: ; ; * firstExcept - which is true until ; encountering the first exception. ; Used to control special code gener- ; ation at the end of the try block. ; ; * hasExAll- which is false until ; the macros encounter an exception ; statement with a blank or zero ; operand (which handles all except- ; ions). ; ; * hasFinally- set true when the macros ; encounter a finally block. Used to ; generate a default finally block if ; the user doesn't specify one. try$SP = -1 ;Stack pointer for try macros try macro local suffix, suffixStk local firstExStk local hasExAllStk local hasFinally local tryName try$SP = try$SP+1 ; Generate a suffix string to attach to labels ; this particular try/except/finally/endtry ; is going to use. Save the suffix on the stack. suffix catstr <$&suffix> suffixStk catstr ,%try$SP %suffixStk textequ <&suffix> ; Emit a label for the start of the try block: tryName catstr , expand2(suffix) tryName:: ; We need to create a stack entry for "firstExcept" ; so we can emit the code at the end of the protected ; block when we encounter the first "except" or ; "finally" clause. firstExStk catstr ,%try$SP firstExStk = 1 ; We need to create a stack entry for "hasExceptAll" ; so we can emit an "catchall" exception sequence ; upon encountering the endtry if there was a finally ; clause and no "accepts all" exception clause. hasExAllStk catstr ,%try$SP hasExAllStk = 0 ; We need to create a stack entry for "hasFinally" ; to determine if we need to emit an empty finally ; section in this try/endtry sequence. hasFinally catstr ,%try$SP hasFinally = 0 endm ;Try ;------------------------------------------------- ; ; exception macro- ; ; Handles the "EXCEPT" clause in a ; try/except/finally/endtry sequence. ; ; The main things this macro must do is generate ; a (global) label for the exception code, ; generate a scope record for the exception, ; and generate a jmp instruction to transfer ; control from the previous block (try or ; exception) to the end of the try statement. ; ; Special cases: ; ; On the first first exception invocation ; (occurring at the end of the try protected ; block), this macro generates special code ; to finish up the protected code (mainly, ; calling the "finally" code). ; ; This is a functional macro (that only ; returns an empty string) so it can use ; the syntax "exception( excode)". ; ; If the exception code argument is blank, ; or if it is zero, that exception must be ; the last one. The exception value zero ; is not allowed in a list of exception codes. ; exception macro excpCode:vararg local suffix, firstExStk local excpName local hasExAll local finallyCode local endTryLbl local endProtected local exNdx, exVal suffix catstr ,%try$SP ; If this is the first except clause for the current ; try invocation, then we need to emit a call to ; the finally code before jumping to the endtry. ; If this is not the first except clause, we ; just emit a jump (the call to the finally ; code was already produced at the beginning of ; the current except claue). firstExStk catstr ,%try$SP finallyCode catstr , expand2(suffix) if firstExStk eq 1 call finallyCode ;For protected block endif ; End of previous protected try block or ; exception block. If we get to this point ; then jump to the end of the try/endtry ; statement: endTryLbl catstr , expand2(suffix) jmp endTryLbl ; If this is the first except block, we need to ; mark the end of the protected block here: if firstExStk eq 1 endProtected catstr , expand2(suffix) endProtected:: endif &firstExStk&= 0 ; For each exception appearing in the exception list, ; generate a scopeTable record. If the list is empty, ; treat it as zero. ifb <&excpCode> excpCode textequ <0> else ; Generate the exception name by appending the exception ; value to "except"+suffix. Note, however, that if there ; are multiple exception values, we must only use the ; first one. exNdx instr <,>, <$&excpCode> if exNdx ne 0 exVal substr <$&excpCode>, 1, exNdx-1 else exVal textequ <&excepCode> endif endif ;ifb excpName catstr , expand2(suffix), <&exVal> excpName:: push rax ;Preserve parameter push rax ; across call. call finallyCode ;For exception block pop rax ;Two pushes and pops pop rax ; to 16-byte align. ; Build the exception records for each exception ; code in the list. Note that if the exception code ; zero appears anywhere except at the end of all ; the exceptions, this macro has to generate ; an error. hasExAll catstr ,%try$SP for exceptionCode, <&excpCode> local ts, te, exc, fc ; If the "any" exception has come along (code=0), ; don't allow any other exceptions. if hasExAll ne 0 echo ***************************** echo Exception() statement not echo allowed after Exception(0). echo ***************************** .err exitm else if exceptionCode eq 0 hasExAll = 1 endif ; Construct the arguments for the buildScope ; macro expansion: ts catstr , expand2(suffix) te catstr , expand2(suffix) fc catstr , expand2(suffix) ; Generate the string to be emitted as the scopeTable ; record when endtry comes along. buildScope exceptionCode, \ ts, \ te, \ excpName, \ fc endif ;hasExAll ne 0 endm ;FOR exitm <> ;Just to force parens endm ;except ;------------------------------------------------ ; ; Handle the finally clause here: finally macro local firstExStk local hasFinally, suffix local finallyLabel, endtryLbl local hasExAll, ts, te, en, fc suffix catstr ,%try$SP hasExAll catstr ,%try$SP ; If there were no exception clauses before this ; finally clause, then we need to emit a call to ; the finally code before jumping to the endtry. ; This is for the try protected block, which ; appears immediately before the finally block. firstExStk catstr ,%try$SP finallyLabel catstr , expand2(suffix) if firstExStk eq 1 call finallyLabel endif ; Whether a try block or an exception block ; preceded this finally block, we've got to ; emit a jump to the endtry label to skip over ; the code in the finally block. endtryLbl catstr , expand2(suffix) jmp endtryLbl ; If there wasn't an exception handler that ; specifically handles *any* exception (exception ; code 0), then we need to create one here. ; If there is a finally block, then we need to ; catch all exceptions so the finally code can ; execute. If we generate a phantom exception(any) ; block, it will execute the finally code and ; then re-raise the exception. if hasExAll eq 0 ; Construct the arguments for the buildScope ; macro expansion: ts catstr , expand2(suffix) te catstr , expand2(suffix) en catstr , expand2(suffix), <$0> fc catstr , expand2(suffix) ; Generate the string to be emitted as the scopeTable ; record when endtry comes along. buildScope ex(any), \ ts, \ te, \ en, \ fc ; Here's the exception entry point. Note that ; the system passes a pointer to the exception ; record in RAX (this was set up in genericLSH). ; Save it while we're calling the finally code ; (finallyLabel). The following pushes RAX ; twice to keep the stack 16-byte aligned. en:: push rax ;Pointer to exception push rax ; record call finallyLabel pop rax pop rax ; Re-raise the exception (as this try/endtry ; block did not have an ex(any) exception clause- ; this code just synthesized it). raxEP textequ <(ExceptionRecord_t ptr [rax])> mov ecx, raxEP.ExceptionCode mov edx, raxEP.ExceptionFlags xor r8, r8 xor r9, r9 call RaiseException ;Does not return endif ;hasExAll ; Okay, immediately before the finally code we've ; got to emit the finally label: finallyLabel:: ; If there were no exception clauses before this ; finally section, we also have to emit a label ; to mark the end of the protected section: if firstExStk eq 1 endProtected catstr , expand2(suffix) endProtected:: endif ; Let everyone know there was a finally clause: hasFinally catstr ,%try$SP hasFinally = 1 endm ;finally ;------------------------------------------------ ; ; Handle the endtry here: endtry macro local suffix, hasFinally, expName local hasExcept, endTryLbl local finallyLbl suffix catstr ,%try$SP hasNoExcept catstr ,%try$SP hasFinally catstr ,%try$SP ; If there wasn't a finally or an exception section, ; we need to report an error: if (hasFinally eq 0) and (hasNoExcept eq 1 ) echo ************************** echo try/endtry without any echo exception or finally echo sections echo ************************** .err endif ; See if there was a finally section. If not, we ; have to emit a dummy one: endTryLbl catstr , expand2(suffix) if hasFinally eq 0 jmp endTryLbl ;Have to skip finally finallyLbl catstr , expand2(suffix) finallyLbl:: ret 0 else ;Had a finally section ret 0 ; so emit return instr endif endTryLbl:: try$SP = try$SP - 1 endm ;Endtry ;------------------------------------------------ ; ; Raise macro- ; ; Raises an exception. Just a wrapper ; around the RaiseException API call. Raise macro exCode:req, exFlags ifdifi <&exCode>, ifdifi <&exCode>, mov ecx,exCode ;Exception code endif endif ifb <&exFlags> xor edx, edx ;Exception flags = 0 else mov edx, exFlags endif xor r8,r8 ;Number of arguments xor r9,r9 ;Pointer to arguments call RaiseException endm ;Raise endif ;aoaExcepts_inc