Object Ctx represents one program block . Whenever a block statement is executed, one Ctx object is pushed on Src.CtxStack and it is popped when the corresponding ENDblock is encounterred.
Proper block nesting must be maintained in noemitting state too.
EUROASM NOWARN=2101
ctx PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
INCLUDEHEAD euroasm.htm, \ Interface (structures, symbols and macros) of other modules used in this source.
ea.htm,eaopt.htm,exp.htm,member.htm,msg.htm,pass.htm,pgm.htm,pgmopt.htm,stm.htm
ctx HEAD ; Start of module interface.
Src.CtxStack.
CTX STRUC
.NamePtr D D ; Pointer to block identifier delocalized name.
.NameSize D D ; Size of block identifier.
.ExpansionNr D D ; Value of dynamic %variable %. which makes unique labels.
.Status D D ; Context boolean properties, see CtxEnc below.
; +10h.
.ObjPtr D D ; Pointer to the corresponding object, depending on ctxAnyType, see CtxEnc below.
.MacPtr D D ; Pointer to MAC during macro definition.
.PreviousSect D D ; ^SSS with section which was current before entering the block.
.BlockSect D D ; ^SSS with section of block-begin pseudoinstruction, possibly changed by AUTOSEGMENT=ON.
; +20h.
.LinePtr D D ; Pointer to the 1st physical line of block-begin statement.
.ChunkPtr D D ; Pointer to CHUNK where .LinePtr belongs to.
.LineNext D D ; Pointer to the end of source line with macroinstruction invocation.
.ChunkNext D D ; Pointer to CHUNK where .LineNext belongs to.
; +30h.
.LineEnd D D ; Pointer behind the 1st physical line of block-begin statement.
.Shift D D ; Ordinal operands number modifier. Default=0, increased by %SHIFT (%MACRO).
.ValBuffer D D ; ^BUFFER keeping values of operands in buffers below until the end of block expansion.
.ObjBuffer D D ; ^BUFFER which contains ^CTX_FOR or ^CTX_MAC object at block expansion.
; +40h.
.OrdBuffer D D ; ^BUFFER of 2*DDs keeping values of ordinal operands at %MACRO/%FOR invocation.
.KeyBuffer D D ; ^BUFFER of 4*DDs keeping keyword operands at macro invocation.
.FrmBuffer D D ; ^BUFFER of 4*DD keeping ordinal and keyword formal parameters at %MACRO/%FOR invocation.
; Their names are without %. In case of ctxFOR this buffer contains just one parameter.
ENDSTRUC CTX
CTX.ObjBuffer contains an object
of CTX_MAC structure. It is created at invocations of a macro.
CTX_MAC STRUC
.LabelPtr D D ; Pointer to the label of macro invocation.
.LabelSize D D ; Size of the label of macro invocation.
.MacroNamePtr D D ; Pointer to the name of macro.
.MacroNameSize D D ; Size of the name of macro.
.ProtoLinePtr D D ; Pointer to the prototype (%MACRO statement).
.InvokStmStatus D D ; STM.Status of the statement which invoked the macro.
ENDSTRUC CTX_MAC
CTX.ObjBuffer contains an object
of CTX_FOR structure.
CTX_FOR STRUC
.OrdinalNr D D ; Curent ordinal number of processed %FOR operand.
.NrOfOrdinals D D ; Maximal ordinal number of %FOR operands.
.Step D Q ; Current STEP value (signed integer). The value is -1 or +1 when flag CTX.Status:ctxStep0 is set.
.Value D Q ; Current formal variable numeric value. Also stored to CTX.FrmBuffer as a string.
.RangeLeft D Q ; Current operand range value. Ignored when the operand is not a range.
.RangeRight D Q ; Current operand range value. Ignored when the operand is not a range.
ENDSTRUC CTX_FOR
ctxAnyType = 0x0000_0FFF ; Block type mask. See also CtxGetEndTypename.CtxEndTypenameTable.
ctxPROGRAM = 0x0000_0001 ; CTX.ObjPtr=^PGM
ctxPROC = 0x0000_0002 ; CTX.ObjPtr=^SYM
ctxPROC1 = 0x0000_0004 ; CTX.ObjPtr=^SYM
ctxSTRUC = 0x0000_0008 ; CTX.ObjPtr=^SSS
ctxMACRO = 0x0000_0010 ; CTX.ObjPtr=^CTX_MAC
ctxFOR = 0x0000_0020 ; CTX.ObjPtr=^CTX_FOR
ctxWHILE = 0x0000_0040 ; CTX.ObjPtr=0
ctxREPEAT = 0x0000_0080 ; CTX.ObjPtr=0
ctxIF = 0x0000_0100 ; CTX.ObjPtr=0
; = 0x0000_0200 ; Unused.
ctxHEAD = 0x0000_0400 ; CTX.ObjPtr=0
ctxCOMMENT = 0x0000_0800 ; CTX.ObjPtr=0
; Other context properties.
ctxDefinition = 0x0000_1000 ; Block is being defined (STRUC, %MACRO).
ctxExpansion = 0x0000_2000 ; Block is being expanded (STRUC, %MACRO, %FOR, %WHILE, %REPEAT).
ctxPrototype = 0x0000_4000 ; %MACRO prototype is being prepared.
ctxMacExpList = 0x0000_8000 ; Macroinstruction entered when LIST=ON,LISTMACRO=ON,
; its expansion should be +listed even when the macro is defined in included file and LISTINCLUDE=OFF.
ctxNamespace = 0x0001_0000 ; This block defines namespace.
ctxExpandable = 0x0002_0000 ; Block has %. property (%FOR,%WHILE,%REPEAT,%MACRO).
ctxFormal = 0x0004_0000 ; Block has formal parameters (%FOR, %MACRO). All buffers reserved.
ctxRepeat = 0x0008_0000 ; This block is %FOR or %WHILE or %REPEAT (see LISTREPEAT).
ctx1stRepeat = 0x0010_0000 ; Repeat block is expanded for the 1st time. Display in listing.
ctxNestingOff = 0x0020_0000 ; This block was specified with key NESTINGCHECK=OFF.
ctxIsRange = 0x0040_0000 ; Current %FOR operand is numeric range.
ctxStep0 = 0x0080_0000 ; Set if STEP=0 or if STEP= was not explicitly specified. Empiric slope will be used.
ctxExited = 0x0100_0000 ; %EXITblock encountered, ignore possible nesting errors.
ctxElsed = 0x0200_0000 ; %ELSE statement was already encountered in this ctxIF.
ctxIfEmit = 0x0400_0000 ; %IF block enterred in emitting status.
; = 0x0800_0000 ; Unused.
ctxPgmPassed = 0x1000_0000 ; PGM has performed at least 1 pass. Used to skip PROGRAM stm in repeated passes.
ctxPgmReturned = 0x2000_0000 ; ENDPROGRAM just returned to its PROGRAM statement. Reset in PseudoPROGRAM.
ctxMacLabeled = 0x4000_0000 ; Macro definition has formal label %: declared in one of its statements.
ctxNoEmit = 0x8000_0000 ; Nonemitting part of block reached, such as false %IF, %COMMENT, EXITed etc.
ENDHEAD ctx ; End of module interface.
[.text]
CtxPeek Procedure CtxType, CtxStackPtr
SUB EAX,EAX
MOV [%ReturnEAX],EAX
MOV EBX,[Src.CtxStack::]
MOV EAX,[%CtxStackPtr]
CMP EBX,1
MOV EDX,[%CtxType]
JC .99:
TEST EAX
JNZ .30:
StackPeekLast EBX
.10: JC .99:
TEST [EAX+CTX.Status],EDX
JNZ .80:
.30: StackPeekPrev EBX,EAX
JMP .10:
.80: MOV [%ReturnEAX],EAX
.99: EndProcedure CtxPeek
CtxGetEndTypename Procedure CtxType
MOV EBX,[%CtxType]
MOV ESI,.CtxEndTypenameTable:
JNSt EBX,ctxExited,.10:
MOV ESI,.CtxExitTypenameTable:
.10:MOV EAX, .Dummy
AND EBX,ctxAnyType
BSF EDX,EBX ; EDX=0 (ctxPROGRAM), 1(ctxPROC), 2(ctxPROC1)..11(ctxCOMMENT)
JZ .80:
CMP EDX,SIZE#.CtxEndTypenameTable /4
JNB .80:
MOV EAX,[ESI+4*EDX]
.80: MOV [%ReturnEAX],EAX
; JMPS .90:
[.data]
ALIGN 4
.Dummy DD 0,0
.CtxEndTypenameTable DD \ ; The order must match CTX.Status:ctxAnyType.
Dict_PseudoENDPROGRAM::,Dict_PseudoENDPROC::, Dict_PseudoENDPROC1::,\
Dict_PseudoENDSTRUC::, Dict_PseudopcENDMACRO::, Dict_PseudopcENDFOR::,\
Dict_PseudopcENDWHILE::,Dict_PseudopcENDREPEAT::,Dict_PseudopcENDIF::, \
.Dummy, Dict_PseudoENDHEAD::, Dict_PseudopcENDCOMMENT::
.CtxExitTypenameTable DD \ ; The order must match CTX.Status:ctxAnyType.
Dict_PseudoENDPROGRAM::,Dict_PseudoENDPROC::, Dict_PseudoENDPROC1::,\
Dict_PseudoENDSTRUC::, Dict_PseudopcEXITMACRO::,Dict_PseudopcEXITFOR::,\
Dict_PseudopcEXITWHILE::,Dict_PseudopcEXITREPEAT::,Dict_PseudopcELSE::, \
.Dummy, Dict_PseudoENDHEAD::, Dict_PseudopcENDCOMMENT::
DS 0*CTX_MAC
[.text]
.90:EndProcedure CtxGetEndTypename
CtxCreate Procedure CtxPtr, CtxStatus, StmPtr
MOV EDI,[%CtxPtr]
MOV EBX,[%StmPtr]
Clear EDI,Size=SIZE#CTX
Invoke EaBufferReserve::,CtxCreate
MOV [EDI+CTX.ValBuffer],EAX ; ValBuffer is always reserved, regardless of ctxExpandable.
MOV EDX,[%CtxStatus]
; Set context name.
MOV ECX,[EBX+STM.LabelSize]
MOV ESI,[EBX+STM.LabelPtr]
JNSt EDX,ctxMACRO,.20:
JNSt EDX,ctxExpansion,.20:
MOV ECX,[EBX+STM.OperationSize] ; Macro invocation has context name in Stm.Operation instead of Stm.Label.
MOV ESI,[EBX+STM.OperationPtr]
JMP .30:
.20: JNSt EDX,ctxREPEAT, .30:
; %REPEAT statement is an exception: it has block name in its 1st ordinal instead of label field.
BufferRetrieve [EBX+STM.OrdBuffer]
JECXZ .50:
MOV ECX,[ESI+4]
MOV ESI,[ESI+0]
StripColons ESI,ECX
.30: TEST ECX
JNZ .40:
JSt EDX,ctxIF|ctxRepeat|ctxCOMMENT|ctxHEAD, .50: ; Those blocks may have no name.
.40: Invoke SymDelocalName::,ESI,ECX,[EDI+CTX.ValBuffer],memberDelocal
BufferRetrieve [EDI+CTX.ValBuffer]
MOV [EDI+CTX.NamePtr],ESI ; Persistent delocalized block name.
MOV [EDI+CTX.NameSize],ECX
.50: MOV EAX,[EBX+STM.LinePtr]
MOV ECX,[EBX+STM.LineEnd]
MOV ESI,[EBX+STM.ChunkPtr]
MOV EBX,[EBX+STM.Section]
MOV [EDI+CTX.LinePtr],EAX
MOV [EDI+CTX.LineEnd],ECX
MOV [EDI+CTX.ChunkPtr],ESI
MOV [EDI+CTX.PreviousSect],EBX
MOV [EDI+CTX.BlockSect],EBX
.60: JNSt EDX,ctxFormal,.80:
Invoke EaBufferReserve::,CtxCreate
MOV [EDI+CTX.ObjBuffer],EAX
Invoke EaBufferReserve::,CtxCreate
MOV [EDI+CTX.OrdBuffer],EAX
Invoke EaBufferReserve::,CtxCreate
MOV [EDI+CTX.KeyBuffer],EAX
Invoke EaBufferReserve::,CtxCreate
MOV [EDI+CTX.FrmBuffer],EAX
.80: MOV [EDI+CTX.Status],EDX
.90: EndProcedure CtxCreate
CtxDestroy Procedure CtxPtr
MOV EBX,[%CtxPtr]
Invoke EaBufferRelease::,[EBX+CTX.ObjBuffer]
Invoke EaBufferRelease::,[EBX+CTX.OrdBuffer]
Invoke EaBufferRelease::,[EBX+CTX.KeyBuffer]
Invoke EaBufferRelease::,[EBX+CTX.FrmBuffer]
Invoke EaBufferRelease::,[EBX+CTX.ValBuffer]
EndProcedure CtxDestroy
CTX_FOR.OrdinalNr and CTX_FOR.Value will be updated, too.
CtxForNext Procedure CtxPtr
RangeRightPtr LocalVar
RangeRightSize LocalVar
CtxForNumber LocalVar Size=20 ; Room for the expanded number.
MOV EBX,[%CtxPtr]
MOV EDI,[EBX+CTX.ObjPtr] ; ^CTX_FOR
JSt [EBX+CTX.Status],ctxIsRange,.60:
.20:MOV EDX,[EDI+CTX_FOR.OrdinalNr] ; Get the next ordinal.
INC EDX
CMP [EDI+CTX_FOR.NrOfOrdinals],EDX
JC .99: ; No more ordinals.
MOV [EDI+CTX_FOR.OrdinalNr],EDX
BufferRetrieve [EBX+CTX.OrdBuffer]
LEA ESI,[ESI+8*EDX-8] ; ESI now points to pair OrdValuePtr,OrdValueSize.
MOV ECX,[ESI+4]
MOV ESI,[ESI]
LEA EDX,[ESI+ECX]
Invoke ExpParseRange::,ESI,EDX
JNC .30: ; If the ordinal is numeric range.
RstSt [EBX+CTX.Status],ctxIsRange
MOV EDI,ESI
MOV EDX,ECX
BufferRetrieve [EBX+CTX.FrmBuffer]
MOV [ESI+8],EDI ; Copy nonrange operand to formal variable value.
MOV [ESI+12],EDX
JMP .90:
.30:SetSt [EBX+CTX.Status],ctxIsRange ; %FOR loop has just enterred the next numeric range operand.
; ESI..EDX is range source notation, EAX points behind the range operator.
SUB EDX,EAX
MOV [%RangeRightPtr],EAX ; Save it for later evaluation.
MOV [%RangeRightSize],EDX
LEA ECX,[EAX-2]
SUB ECX,ESI
Invoke ExpEvalNum::,ESI,ECX ; RangeLeft evaluation.
JC .20: ; If syntax error, report Msg and go to the next operand.
MOV [EDI+CTX_FOR.RangeLeft+0],EAX
MOV [EDI+CTX_FOR.RangeLeft+4],EDX
Invoke ExpEvalNum::,[%RangeRightPtr],[%RangeRightSize]
JC .20: ; If syntax error, report Msg and go to the next operand.
MOV [EDI+CTX_FOR.RangeRight+0],EAX
MOV [EDI+CTX_FOR.RangeRight+4],EDX
JNSt [EBX+CTX.Status],ctxStep0,.40:
; If STEP=0, it will be actually +1 or -1.
SUB ECX,ECX
SUB EAX,[EDI+CTX_FOR.RangeLeft+0]
SBB EDX,[EDI+CTX_FOR.RangeLeft+4]
JS .35: ; If range slope negative.
MOV [EDI+CTX_FOR.Step+4],ECX
INC ECX
MOV [EDI+CTX_FOR.Step+0],ECX ; Step:= +1.
JMPS .40:
.35:DEC ECX ; Negative range slope.
MOV [EDI+CTX_FOR.Step+0],ECX
MOV [EDI+CTX_FOR.Step+4],ECX ; Step:= -1.
.40:JSt [EBX+CTX.Status],ctxStep0,.50:
; If nonzero range slope differs from the step sign, this operand will be skipped.
MOV EAX,[EDI+CTX_FOR.RangeRight+0]
MOV EDX,[EDI+CTX_FOR.RangeRight+4]
SUB EAX,[EDI+CTX_FOR.RangeLeft+0]
SBB EDX,[EDI+CTX_FOR.RangeLeft+4]
JS .45: ; If the range slope is negative.
JNZ .43: ; If the range slope is positive.
TEST EAX
JZ .50: ; If the range slope is zero, e.g. "1..1"
.43:; Range slope is positive.
TESTB [EDI+CTX_FOR.Step+7],0x80
JNZ .20: ; Ignore this range when the step is negative.
JMPS .50:
.45:TESTB [EDI+CTX_FOR.Step+7],0x80
JZ .20: ; If range slope is negative and step is positive, skip the operand.
.50:MOV EAX,[EDI+CTX_FOR.RangeLeft+0] ; Start with the left range value.
MOV EDX,[EDI+CTX_FOR.RangeLeft+4]
MOV [EDI+CTX_FOR.Value+0],EAX
MOV [EDI+CTX_FOR.Value+4],EDX
JMP .80:
.60: ; %FOR processing is inside a numeric range.
MOV EAX,[EDI+CTX_FOR.Value+0]
MOV EDX,[EDI+CTX_FOR.Value+4]
ADD EAX,[EDI+CTX_FOR.Step+0]
ADC EDX,[EDI+CTX_FOR.Step+4]
MOV [EDI+CTX_FOR.Value+0],EAX
MOV [EDI+CTX_FOR.Value+4],EDX
TESTB [EDI+CTX_FOR.Step+7],0x80
JNZ .70:
CMP EDX,[EDI+CTX_FOR.RangeRight+4] ; Step is positive. EDX:EAX is the incremented formal value.
JL .80:
JG .20: ; Out of range, continue with the next operand.
CMP EAX,[EDI+CTX_FOR.RangeRight+0]
JBE .80:
JMP .20: ; Out of range, continue with the next operand.
.70:CMP EDX,[EDI+CTX_FOR.RangeRight+4] ; Step is negative. EDX:EAX is the decremented formal value.
JG .80:
JL .20: ; Out of range, continue with the next operand.
CMP EAX,[EDI+CTX_FOR.RangeRight+0]
JB .20: ; Out of range, continue with the next operand.
.80: ; Convert CTX_FOR.Value to decadic into CTX.KeyBuffer and update CTX.FrmBuffer.
BufferClear [EBX+CTX.KeyBuffer]
LEA ECX,[%CtxForNumber]
MOV EAX,[EDI+CTX_FOR.Value+0]
MOV EDX,[EDI+CTX_FOR.Value+4]
StoQD ECX,Size=20,Signed=yes
SUB EDI,ECX ; Context.KeyBuffer will be misused to persistently keep the formal value.
BufferStore [EBX+CTX.KeyBuffer],ECX,EDI
BufferRetrieve [EBX+CTX.KeyBuffer]
MOV EAX,ESI
BufferRetrieve [EBX+CTX.FrmBuffer]
MOV [ESI+8],EAX ; Pointer within KeyBuffer.
MOV [ESI+12],EDI ; Number of digits in decadic value.
.90:CLC
.99:EndProcedure CtxForNext
CtxExpansionNrUpdate Procedure CtxPtr, StmPtr
MOV EBX,[%StmPtr]
MOV EDI,[%CtxPtr]
MOV ESI,[EBX+STM.Program]
TEST ESI
JZ .90:
MOV ECX,[ESI+PGM.PassPtr]
JECXZ .90:
MOV EAX,[ECX+PASS.ExpansionNr]
INC EAX
MOV [ECX+PASS.ExpansionNr],EAX
MOV [EDI+CTX.ExpansionNr],EAX
LEA ESI,[ESI+PGM.Pgmopt]
DEC EAX
MOV ECX,[ESI+PGMOPT.MaxExpansions]
CMP EAX,ECX
CMC
Msg cc=E,'7140',ECX ; Number of expansions exceeded MAXEXPANSIONS=!1D.
.90:EndProcedure CtxExpansionNrUpdate
ctx1stRepeat if all those underlying contexts
have ctx1stRepeat set. Otherwise it returns 0.
Ctx1stRepeat Procedure
SUB EAX,EAX
MOV EDX,ctx1stRepeat
.10:Invoke CtxPeek,ctxRepeat,EAX
JC .90:
JSt [EAX+CTX.Status],ctx1stRepeat,.10:
XOR EDX,EDX
.90:MOV [%ReturnEAX],EDX
EndProcedure Ctx1stRepeat
CtxStatusAll Procedure
MOV EBX,[Src.CtxStack::]
XOR ECX,ECX
StackPeekLast EBX
JC .90:
OR ECX,[EAX+CTX.Status]
.20:StackPeekPrev EBX,EAX
JC .90:
OR ECX,[EAX+CTX.Status]
JMP .20:
.90:MOV [%ReturnEAX],ECX
EndProcedure CtxStatusAll
CtxFind Procedure CtxType, StmPtr
MOVD [%ReturnEAX],0
MOV EDX,[%CtxType]
MOV EBX,[%StmPtr]
StackPeekLast [Src.CtxStack::]
JMPS .20:
.10:StackPeekPrev [Src.CtxStack::],EDI
.20:MOV CL,-1 ; Signalize that no matching blockId was found.
JC .70: ; If the context stack is empty.
MOV EDI,EAX
JNSt [EDI+CTX.Status],EDX,.10:
CMPD [%ReturnEAX],0
JNZ .30:
MOV [%ReturnEAX],EDI ; Prepare to return the first context found for the case of not-matching blockId.
.30:TEST EBX ; EDI is context of desired type. If StmPtr provided, check blockId.
JZ .80: ; If blockId match cannot be tested, the 1st context will be used.
MOV ESI,[EBX+STM.LabelPtr]
MOV ECX,[EBX+STM.LabelSize]
CMPD [EBX+STM.OperationData],PseudopcENDREPEAT::, IMM=DWORD
JE .40:
CMPD [EBX+STM.OperationData],PseudopcUNTIL::, IMM=DWORD
JE .40:
BufferRetrieve [EBX+STM.OrdBuffer] ; Other than %ENDREPEAT have blockId in 1st ordinal.
TEST ECX
JZ .80: ; Empty ENDblockId is always matching.
MOV ECX,[ESI+4]
MOV ESI,[ESI+0]
StripColons ESI,ECX ; Get rid of trailing colon(s), if any.
.40:TEST ECX ; ESI,ECX is now the ENDblock identifier.
JZ .80: ; Empty ENDblockId is always matching.
Invoke EaBufferReserve::,CtxFind
Invoke SymDelocalName::,ESI,ECX,EAX,memberDelocalParent ; In case that block name has local scope.
BufferRetrieve EAX
Invoke EaBufferRelease::,EAX
.50:Compare [EDI+CTX.NamePtr],[EDI+CTX.NameSize],ESI,ECX
MOV CL,0 ; Mark matching blockId found.
JE .80: ; If blockId match, done. ZF=1.
JMP .10: ; Otherwise try to find a better context.
.70:; Stack peeked but no matching blockId found.
MOV EDI,[%ReturnEAX]
.80:MOV [%ReturnEAX],EDI ; EDI=0 if no CtxType found. CL=0 if blockId match.
TEST EDI
STC
JZ .90: ; Return CF=ZF=1 if no such context is on stack.
TEST CL ; Return CF=0, ZF=1 if blockId match, otherwise CF=ZF=0.
.90:EndProcedure CtxFind
Stm.CtxStatusAll:ctxExited, report E7110 or E7120 on each block mismatch detected.
CtxDiscard Procedure CtxPtr, StmPtr
CtxDbuffer LocalVar ; Temporary buffer for contextes with NESTINGCHECK=OFF.
Invoke EaBufferReserve::,CtxDiscard
MOV [%CtxDbuffer],EAX
MOV ESI,[%CtxPtr]
MOV EBX,[%StmPtr]
TEST ESI
JZ .80:
MOV EDX,[ESI+CTX.Status]
AND EDX,ctxAnyType
.10:StackPop [Src.CtxStack::]
JC .60:
MOV EDI,EAX
CMP EDI,[%CtxPtr]
JE .50: ; If this is the context to discard.
JSt [EBX+STM.CtxStatusAll],ctxExited,.50: ; On EXITblock do not report mismatch.
JNSt [EBX+STM.CtxStatusAll],ctxNestingOff,.20:
; If any context on stack has ctxNestingOff, do not report mismatch and
; do not remove contexts above the one which is being discarded. Store them to CtxDbuffer instead.
BufferStore [%CtxDbuffer],EDI,SIZE#CTX ; Silently save it to be pushed back later in .70:.
JMP .10:
.20:JNSt [EBX+STM.CtxStatusAll],ctxRepeat,.30:
JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.50:
.30:Invoke CtxGetEndTypename,[EDI+CTX.Status]
JNSt [EDI+CTX.Status],ctxREPEAT,.40:
XCHG EAX,EDI
.40:Msg '7110',EAX,EDI ; Wrong nesting, expected "!1S !2S".',0
.50:Invoke CtxDestroy,EDI
CMP EDI,[%CtxPtr]
JNE .10:
.60:BufferRetrieve [%CtxDbuffer] ; Zero or more undestroyed CTX objects.
.70:LEA EDI,[ESI+ECX-SIZE#CTX] ; The last one.
CMP EDI,ESI
JB .80: ; If no more saved contexts.
StackPush [Src.CtxStack::],EDI ; Return contexts back to stack in original order.
SUB ECX,SIZE#CTX
JMP .70:
.80:Invoke EaBufferRelease::,[%CtxDbuffer]
.90:EndProcedure CtxDiscard
ENDPROGRAM ctx