Following procedures – pseudoinstruction handlers – do the actual pseudoinstruction job.
They are called with statement parsed from source text into
STM structure. Pseudoinstruction handlers are called
regardless of CtxStatus:ctxNoEmit
state. In nonemitting state
the handler does nothing unless it is the same block/endblock type, in which case it updates the context.
EUROASM NOWARN=2101, pseudo PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32,MAXPASSES=64 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules. INCLUDEHEAD \ ; Include headers of another modules used in this module. ctx.htm, \ chunk.htm, \ dict.htm, \ ea.htm, \ eaopt.htm, \ exp.htm, \ ii.htm, \ lst.htm, \ mac.htm, \ member.htm, \ msg.htm, \ pass.htm, \ pgm.htm, \ pgmopt.htm, \ reloc.htm, \ sss.htm, \ stm.htm, \ sym.htm, \ syswin.htm, \ ;;
pseudo HEAD ; This modul has no interface. ENDHEAD pseudo
PseudoALIGN Procedure Stm Align1 LocalVar ; 1.argument value 0..512. Must be power of 2. Align2 LocalVar ; 2.argument value 0..511. Must be below %Align1. MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: MOV EDI,[EBX+STM.Program] TEST EDI JZ .90: MOV ESI,[EDI+PGM.CurrentSect] ; ALIGN does not change autosegment value. MOV EAX,[ESI+SSS.OrgLow] MOV EDX,[ESI+SSS.OrgHigh] MOV [EBX+STM.Section],ESI MOV [EBX+STM.OffsetLow],EAX MOV [EBX+STM.OffsetHigh],EDX Invoke StmCheckFields::, EBX,"00+0" BufferRetrieve [EBX+STM.OrdBuffer] TEST ECX JZ .90: ; If no ordinals, do nothing. LODSD ; Ptr to %1. MOV EDI,EAX LODSD ; Size of %1. MOV EDX,[EBX+STM.Section] Invoke ExpParseAlignment::,EDI,EAX,EDX JC .90: MOV [%Align1],EAX MOVD [%Align2],0 BufferRetrieve [EBX+STM.OrdBuffer] CMP ECX,16 Msg cc=A,'3413' ; Pseudoinstruction ALIGN expects one or two operands. JB .30: ; If one operand only. ; ALIGN with two operands. ADD ESI,8 ; Skip %1. LODSD MOV EDI,EAX LODSD Invoke ExpParseAlignment::,EDI,EAX,0 MOV [%Align2],EAX JC .30: ; Ignore wrong %2. CMP EAX,EDX ; EAX is value of 2nd operand. It should be below 1st operand. Msg cc=NB,'3412',EDX,EAX ; Value of the 2nd operand (!2D) should be below the 1st operand (!1D). Ignored. .30:Invoke ExpAlign::,[EBX+STM.OffsetLow],[%Align1],[%Align2] ; Return ECX=size of alignment stuff. JECXZ .90: ; No alignment necessary. MOV EDI,[EBX+STM.Program] XOR EDX,EDX ; DL=alignment stuff. Invoke SssEmitAlignment::,[EDI+PGM.CurrentSect],ECX,[EBX+STM.EmitBuffer] .90:EndProcedure PseudoALIGN
Stm.Status
initialized with D mnemonic suffix (B,U,W,D,Q,T,O,Y,Z,I,S) or 0 if no suffix is used.
Operands without explicit datatype specification will inherit datatype from D suffix.
Datatype of quoted string without explicit specification (e.g. D "string"
)
is 'B' or 'U', depending on EUROASM
%^UNICODE option, if no explicit datatype is specified by D suffix.PseudoData Procedure Stm DataExp LocalVar Size=SIZE#EXP ; EXP object used to parse the ordinal operand of Stm, such as4*D 0
. OrdPtr LocalVar ; Pointer to the operand text. OrdSize LocalVar ; Size of the operand text. OrdBufPtr LocalVar ; Pointer to an array of pairs of DWORD with ordinal operand (%OrdPtr,%OrdSize). OrdBufSize LocalVar ; 0,8,16,.. depending on the number of ordinals in the statement. DataEmitBuf LocalVar ; ^BUFFER for data emitted by D pseudoinstruction. Empty in BSS section. DataRelocBuf LocalVar ; ^BUFFER for RELOC records required by D pseudoinstruction (in the final pass). StrucEmitBuf LocalVar ; ^BUFFER for data emitted by a structure member. StrucRelocBuf LocalVar ; ^BUFFER for RELOC records required by a structure member. MOV EBX,[%Stm] LEA EDI,[%DataExp] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: ; Do nothing in nonemitting state. Invoke EaBufferReserve::,%^PROC MOV [%DataEmitBuf],EAX Invoke EaBufferReserve::,%^PROC MOV [%DataRelocBuf],EAX Invoke EaBufferReserve::,%^PROC MOV [%StrucEmitBuf],EAX Invoke EaBufferReserve::,%^PROC MOV [%StrucRelocBuf],EAX RstSt [EBX+STM.Flags],stmtNotBSS ; Any initialized static data found in ExpEvalData may set this flag later. BufferRetrieve [EBX+STM.OrdBuffer] ; Array of 2*DWORDs (pointer to ordinal + size of ordinal). MOV [%OrdBufPtr],ESI MOV [%OrdBufSize],ECX .10:; The code between .10: and .22: is a loop for processing all ordinal operands. MOV ECX,[%OrdBufSize] MOV ESI,[%OrdBufPtr] TEST ECX JZ .22: ; If no more ordinal operands follows. LODSD ; EAX=pointer to the source text of the operand. MOV [%OrdPtr],EAX MOV EDX,EAX LODSD ; Size of the text of the operand. MOV [%OrdSize],EAX MOV [%OrdBufPtr],ESI ; Prepare pointer for the next ordinal, if any. SUBD [%OrdBufSize],8 TEST EAX JZ .10: ; Skip an empty operand. Invoke ExpParseData::,EDI,EDX,EAX ; Parse the source data EDX,EAX to expression EDI. JC .10: ; Skip the operand if error was detected and reported. MOV EDX,[EDI+EXP.Status] ; DL='D', DH=datatype specified in ordinal operand ('B','W','S','I' etc) or 0. MOV EAX,[EBX+STM.Status] ; AL=datatype specified as D-mnemonic suffix. SHR EDX,8 ; DL=ordinal datatype. TEST DL JNZ .12: MOV DL,AL ; Use D-mnemonic suffix if the ordinal specified a typeless data value, e.g. inDQ 3.14
. .12:TEST AL JNZ .16: .14:MOV [EBX+STM.Status],DL ; Use 1st ordinal type as a default for all following typeless operands, if no Dsuffix, e.g.D "string",13,10
. TEST DL JNZ .16: JNSt [EDI+EXP.Status],expString,.16: MOV DL,'U' ; String without datatype, e.g.D "string"
. Its datatype is either 'B' or 'U'. JSt [Ea.Eaopt.Status::],eaoptUNICODE,.14: MOV DL,'B' JMP .14: .16:CMP DL,'S' JE .40: ; If the ordinal defines a structured object, e.g.D 2*SomeStruc
, goto .40:. ; Evaluate and emit ordinal nonstructured data from [%Ord] to [%DataEmitBuf],[%DataRelocBuf] (none of operands is a structured variable). ; Input: EBX=^STM, EDI=^EXP, DL=default datatype. Invoke StmCheckFields::,EBX,"?0*0" ; Many ordinals but no keyword is permitted in nonstructured D. JC .74: BufferClear [%DataEmitBuf] BufferClear [%DataRelocBuf] Invoke ExpEvalData::,[%DataEmitBuf],[%DataRelocBuf],[%OrdPtr],[%OrdSize],EDX,EBX ; This might set Stm.Flags:stmtNotBSS. BufferRetrieve [%DataRelocBuf] ; ESI,ECX contains zero or more RELOC records. JECXZ .20: ; One or more relocation appeared in this ordinal. Their origins must be patched ; by the size of data emitted so far in this statement, and then stored to Stm.RelocBuffer. .18:PUSH ECX PUSH ESI BufferRetrieve [EBX+STM.EmitBuffer] ; Get the size of emited data to ECX. POP ESI ADD [ESI+RELOC.OrgLow],ECX ; Patch the relocation origin. BufferStore [EBX+STM.RelocBuffer],ESI,SIZE#RELOC ; Save the relocation ESI to the statement object. POP ECX ADD ESI,SIZE#RELOC SUB ECX,SIZE#RELOC JA .18: ; If there are more relocations in this ordinal (duplicated data value was used). .20:BufferRetrieve [%DataEmitBuf] ; Emitted or reserved data of this operand. BufferStore [EBX+STM.EmitBuffer],ESI,ECX ; Save them to the statement. JMP .10: ; The next ordinal operand of pseudoinstruction D. .22:; All operands are processed, now decide what kind of section (CODE, DATA or BSS) ; should be actually used when AUTOSEGMENT=ON. This depends onStm.Flags:stmtNotBSS
flag. MOV EDI,[EBX+STM.Section] ; First assume that current section will not change. MOV ECX,sssPurposeCODE CMPB [EBX+STM.Status],'I' ; Data defined with pseudoinstruction DI go to a CODE section. JE .24: MOV ECX,sssPurposeDATA JSt [EBX+STM.Flags],stmtNotBSS,.24: ; If some of the ordinals had initialized data value, it goes to DATA section. MOV ECX,sssPurposeBSS|sssPurposeSTACK ; Otherwise all data are uninitialized. This will prefer BSS or STACK segment. .24:JSt [EBX+STM.CtxStatusAll],ctxSTRUC,.30: ; Inside structure definition do not change section. TEST EDI JZ .26: ; When no current section is specified, act as if AUTOSEGMENT=ON. JNSt [Ea.Eaopt.Status::],eaoptAUTOSEGMENT,.30: ; Do not change section when AUTOSEGMENT=OFF. JSt [EDI+SSS.Purpose],ECX,.30: ; If the purpose of current section complies, do not change the section. ; AUTOSEGMENT=ON and the current section does not have the required purpose ECX. Let's switch to another section. .26:Invoke SssGetSegm::,[EBX+STM.Program],ECX ; Find a segment with purpose ECX. JNC .28: ; If required section was found. RstSt ECX,sssPurposeSTACK ; If requested purpose was BSS|STACK, report only BSS. Invoke DictSearchByData::,DictSegmentPurpose::,ECX ; Returns purpose name Qword in ESI, to be used in error msg. Msg '3201',ESI ; No segment with PURPOSE=!1S found. MOV EAX,EDI .28:MOV EDI,EAX .30:MOV [EBX+STM.Section],EDI ; Data will be emitted|reserved in this section|structure EDI. TEST EDI JZ .66: JNSt [EBX+STM.Flags],stmtNotBSS,.32: SetSt [EDI+SSS.Status],sssNotBSS .32:JNSt [EDI+SSS.Status],sssSegment|sssSection,.34: SetSt [EDI+SSS.Status],sssUsed ; Mark the section as dirty. Invoke SssCheckPurpose::,EDI,ECX ; Warn if wrong section chosen (either auto or manually). .34:MOV EAX,[EDI+SSS.OrgLow] MOV EDX,[EDI+SSS.OrgHigh] JNSt [Ea.Eaopt.Status::],eaoptAUTOALIGN,.38: JSt [EBX+STM.CtxStatusAll],ctxSTRUC,.38: ; Do not autoalign inside structure definition. ; Data will be autoaligned by the default datatype (by Dsuffix, otherwise by %1 datatype). PUSH EAX ; Do not clobber section's offset in EDX:EAX. LEA ECX,[EBX+STM.Status] ; ECX is a pointer to the suffix 'B','U','W'... Invoke DictLookup::,DictAlignValue::,ECX,1 MOV ECX,0 ; Prepare number of stuff bytes. JC .36: ; No alignment found. Invoke ExpAlign::,[EDI+SSS.OrgLow],EAX,0 ; EAX is the required data alignment 1,2,4,8,16,32,64. MOV [EBX+STM.AlignBytes],ECX ; ECX=Number of stuff bytes 0..63. .36:POP EAX ADD EAX,ECX ADC EDX,0 .38:MOV [EBX+STM.OffsetLow],EAX ; (AutoAligned) origin of this statement. MOV [EBX+STM.OffsetHigh],EDX JMP .66: ; Continue at the epilogue code. .E6122:Msg PgmStatus=pgmLastPass,'6122',EAX ; Structure "!1S" not found. JMP .50: ; If not emitting an existing declared structure. .40:; Evaluate and emit structured data, e.g.Lbl DS 2*MyStructure, .Member1=Value1,.Member2=Value2"
. ; Input: EBX=^STM, EDI=^EXP, DL='S'. Invoke StmCheckFields::,EBX,"?01*" ; Only one ordinal but many keywords are permitted in DS statement. MOV EAX,symDefined ; For the case of error E4810..E6840. JC .74: MOV EDX,[EBX+STM.Section] ; Current program section. BufferRetrieve [EBX+STM.OrdBuffer] MOV EAX,ECX ; Prepare !1S for E6122, e.g.MyStruc: DS ; No structure specified.
JECXZ .E6122: ; Structure "!1S" not found. MOV EAX,ESI ; Pointer to 2*DWORD (ptr,size) of %1, i.e. name of the structure. MOV ESI,[EDI+EXP.Seg] ; ESI=^SSS structure, or 0 in pass1 if it's forward referenced. TEST ESI JZ .E6122: ; Structure "!1S" not found. JNSt [ESI+SSS.Status],sssNotBSS,.42: SetSt [EBX+STM.Flags],stmtNotBSS .42:SetSt [ESI+SSS.Status],sssUsed MOV EAX,[EBX+STM.Program] MOV ECX,[ESI+SSS.Purpose] MOV EDX,[EAX+PGM.CurrentSect] CMPD [EBX+STM.NrOfKeys],0 ; Update some members of the structure with static data? JNA .44: ; Skip if no member is statically initialized. SetSt [EBX+STM.Flags],stmtNotBSS ; Some structure members are initialized with a keyword. e.g.DS aStruc, .Member=5
. SetSt ECX,sssPurposeDATA ; At least one member is being updated, RstSt ECX,sssPurposeBSS ; hence the structure should be emitted to data section. .44:JSt [EBX+STM.CtxStatusAll],ctxSTRUC,.50: ; Do not change section inside STRUC/ENDSTRUC block. JNSt [Ea.Eaopt.Status::],eaoptAUTOSEGMENT,.50: ; Do not change section if AUTOSEGMENT=OFF. TEST EDX ; Test if the current section is specified. JZ .46: JSt [EDX+SSS.Purpose],ECX,.50: ; If the purpose of current section complies. .46:; Current section EDX does not have the required purpose ECX, try to switch to another section. Invoke SssGetSegm::,[EBX+STM.Program],ECX ; Returns pointer to a segment in EAX. JNC .48: ; If segment autochanged successfully. Msg '3201',Dict_PurposeDATA:: ; No segment with PURPOSE=!1S found. MOV EAX,EDX ; Keep the current section in this case. .48:MOV EDX,EAX .50:MOV [EBX+STM.Section],EDX ; EDX=current or autoselected section where to emit data. TEST EDX JZ .66: MOV EDI,EDX ; EDX=EDI is section where the structured member will be emitted to. JNSt [EDI+SSS.Status],sssSegment|sssSection,.52: Invoke SssCheckPurpose::,EDI,ECX ; Warn if wrong section purpose. .52:MOV EAX,[EDI+SSS.OrgLow] MOV EDX,[EDI+SSS.OrgHigh] JNSt [Ea.Eaopt.Status::],eaoptAUTOALIGN,.58: ; Skip if AUTOALIGN=OFF. ; Structured memory object specified with pseudoinstruction DS will be autoaligned ; by the structure's own alignment, if it is already known. MOV ECX,[EDI+SSS.Alignment] ; Structure of the object which is being defined. TEST ESI ; ^SSS sssStructure. JZ .54: MOV ECX,[ESI+SSS.Alignment] ; Structure which is being instanceized. .54:Invoke ExpAlign::,[EDI+SSS.OrgLow],ECX,0 MOV [EBX+STM.AlignBytes],ECX ; Number of stuff bytes. ADD EAX,ECX ADC EDX,0 .58:MOV [EBX+STM.OffsetLow],EAX ; AutoAligned origin of this statement. MOV [EBX+STM.OffsetHigh],EDX ; Structure contents will be duplicated to Stm.EmitBuffer. LEA EAX,[%DataExp] MOV EDX,[EAX+EXP.Seg] ; EDX=^SSS with emitting structure. ; Relocations specified in structure definition will be copied to Stm.RelocBuffer ; and patched by the amount of so far emitted data in Stm.EmitBuffer, which is temporary kept in Stm.Size. CMPD [EAX+EXP.High],0 ; Duplicator. JZ .66: ; Emit nothing but symbol and autoalignment if zero duplicator, e.g.MyData DS 0*MyStruc
. .60:; Otherwise emitted data in SSS.EmitBuffer will be updated by statement keywords and stored to Stm. ; Entire structure emit-data will be copied to temporary [%DataEmitBuf] ; and, if some statement keywords specified, corresponing member(s) will be rewritten. BufferClear [%StrucEmitBuf] BufferClear [%StrucRelocBuf] Invoke MemberUpdate::,EDX,EBX,[%StrucEmitBuf],[%StrucRelocBuf] BufferRetrieve [%StrucRelocBuf] JECXZ .64: .62:MOV EAX,[EBX+STM.Size] ADD [ESI+RELOC.OrgLow],EAX ; Patch and store relocations from this duplication. ADCD [ESI+RELOC.OrgHigh],0 BufferStore [EBX+STM.RelocBuffer],ESI,SIZE#RELOC ADD ESI,SIZE#RELOC SUB ECX,SIZE#RELOC JA .62: .64:BufferRetrieve [%StrucEmitBuf] ADD [EBX+STM.Size],ECX BufferStore [EBX+STM.EmitBuffer],ESI,ECX ; Emit updated struc contents. LEA EAX,[%DataExp] DECD [EAX+EXP.High] ; Duplicator. JNZ .60: ; Next duplication of the whole structure. JNSt [EBX+STM.Status],stmLabelPresent,.66: ; If label is present and duplicator nonzero, further duplications are ignored ; and each member of the structure will create symbol in temporary struc expansion context. StackPush [Src.CtxStack::],0 Invoke CtxCreate::,EAX,ctxSTRUC+ctxExpansion+ctxNamespace,EBX Invoke MemberCreate::,EDX,EBX StackPop [Src.CtxStack::] .66:; Common epilogue for structured and nonstructured data. BufferRetrieve [EBX+STM.EmitBuffer] MOV [EBX+STM.Size],ECX ; SIZE# attribute of statement's symbol, if defined. MOV EDX,[EBX+STM.Status] CMP DL,0 MOV DL,'A' ; Operation D without data defaults to datatype 'A' (address). JNZ .68: MOV [EBX+STM.Status],EDX ; Attribute TYPE# of statement's symbol, if defined. .68:; Maintain sssNotBSS and structrure member when in structure definition. MOV ECX,[EBX+STM.Program] JECXZ .72: MOV EDI,[ECX+PGM.CurrentSect] TEST EDI JZ .72: JNSt [EDI+SSS.Status],sssStructure,.72: ; If not inside a structure definition. MOVD [EDI+SSS.Purpose],sssPurposeBSS ; Purpose of a structure reflects whether it declares static data or not. JNSt [EDI+SSS.Status],sssNotBSS,.70: MOVD [EDI+SSS.Purpose],sssPurposeDATA ; Mark that the structure declaration has static data. .70:JNSt [EBX+STM.Status],stmLabelPresent,.80: LEA EAX,[%DataExp] MOV ECX,[EAX+EXP.Seg] Invoke MemberAdd::,EDI,EBX,ECX .72:; Define symbol if used in pseudoinstruction D. JNSt [EBX+STM.Status],stmLabelPresent,.80: MOV EAX,symDefined JNSt [EBX+STM.Status],stmLabelIsPublic,.74: OR EAX,symGlobalRef .74:Invoke SymCreate::,EAX,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EBX .80:Invoke EaBufferRelease::,[%DataEmitBuf] Invoke EaBufferRelease::,[%DataRelocBuf] Invoke EaBufferRelease::,[%StrucEmitBuf] Invoke EaBufferRelease::,[%StrucRelocBuf] .90:EndProcedure PseudoData
PseudoD Procedure Stm MOV EBX,[%Stm] Invoke PseudoData,EBX .90:EndProcedure PseudoD
PseudoDB Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'B' Invoke PseudoData,EBX EndProcedure PseudoDB
PseudoDU Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'U' Invoke PseudoData,EBX EndProcedure PseudoDU
PseudoDW Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'W' Invoke PseudoData,EBX EndProcedure PseudoDW
PseudoDD Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'D' Invoke PseudoData,EBX EndProcedure PseudoDD
PseudoDQ Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'Q' Invoke PseudoData,EBX EndProcedure PseudoDQ
PseudoDT Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'T' Invoke PseudoData,EBX EndProcedure PseudoDT
PseudoDO Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'O' Invoke PseudoData,EBX EndProcedure PseudoDO
PseudoDY Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'Y' Invoke PseudoData,EBX EndProcedure PseudoDY
PseudoDZ Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'Z' Invoke PseudoData,EBX EndProcedure PseudoDZ
PseudoDI Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'I' Invoke PseudoData,EBX EndProcedure PseudoDI
PseudoDS Procedure Stm MOV EBX,[%Stm] MOVB [EBX+STM.Status],'S' Invoke PseudoData,EBX EndProcedure PseudoDS
PseudoENDHEAD Procedure Stm MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. Invoke StmCheckFields::, EBX,"00?0" .90:EndProcedure PseudoENDHEAD
PseudoEUROASM Procedure Stm MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::,EBX,'00**' SetSt [Src.Lst.Status::],lstNoData ; EUROASM statement may have ordinal operands PUSH and POP. BufferRetrieve [EBX+STM.OrdBuffer] LEA EDX,[ESI+ECX] .10: CMP ESI,EDX JNB .30: ; If no more ordinals. LODSD ; Ordinal operand. MOV EDI,EAX LODSD MOV ECX,EAX Invoke DictLookup::, DictEuroasmOrd::, EDI,ECX JNC .20: LEA EAX,[ESI-8] Msg cc=C,'7810',EAX,DictEuroasmOrd:: ;Unknown EUROASM ordinal "!1S". Expected one of !2L. JMP .10: .20: JMP EAX ; .PUSH or .POP. .PUSH::StackPush [Src.EaoptStack::], Ea.Eaopt:: JMP .10: .POP::StackPop [Src.EaoptStack::] Msg cc=C,'3812' ; EUROASM POP without EUROASM PUSH. Ignored. JC .10: CopyTo Ea.Eaopt::,EAX,Size=SIZE#EAOPT JMP .10: .30: ; Handle all EUROASM keyword operands. BufferRetrieve [EBX+STM.KeyBuffer] LEA EDX,[ESI+ECX] .40: CMP ESI,EDX JNB .99: ; If no more keywords. Invoke EaoptAssemble::, Ea.Eaopt::, [ESI+0],[ESI+4],[ESI+8],[ESI+12] ADD ESI,16 JMP .40: .99: EndProcedure PseudoEUROASM
PseudoHEAD Procedure Stm MOV EBX,[%Stm] StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. SetSt [Src.Lst.Status::],lstNoData JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: Invoke StmCheckFields::, EBX,"*000" .90:EndProcedure PseudoHEAD
PseudoNoOperation Procedure Stm MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: JNSt [EBX+STM.Status],stmLabelPresent|stmPrefixPresent|stmOperationPresent,.90: ; Skip empty statement. MOV EDI,[EBX+STM.Program] TEST EDI JZ .90: MOV EAX,[EDI+PGM.CurrentSect] TEST EAX JZ .10: MOV [EBX+STM.Section],EAX .10: JNSt [EBX+STM.Status],stmLabelPresent, .50: MOV ESI,[EBX+STM.LabelPtr] CMPB [ESI],'[' JNE .50: ; The statement is [new_section_definition] or [existing_section_switch], e.g.[NewSect]
. StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. Invoke StmCheckFields::,EBX,"1000" JC .90: MOV ECX,[EBX+STM.LabelSize] CMPB [ESI+ECX-1],']' JE .20: .E7831:Msg '7831',EBX ; Segment or section name "!1S" must be in braces []. JMP .90: .20: INC ESI SUB ECX,2 ; Strip []. StripSpaces ESI,ECX TEST ECX JZ .E7831: ; Segment or section name "!1S" must be in braces []. RstSt [Ea.Eaopt.Status::],eaoptAUTOSEGMENT ; AUTOSEGMENT is silently switched OFF by any statement which changes section. Invoke SssFindByName::,sssSection|sssSegment,0,ESI,ECX,0 JNC .40: ; Skip when an old section is being switched to. ; New section ESI,ECX is being declared in current segment. MOV EAX,[EBX+STM.Section] ; Previous section. TEST EAX JZ .30: MOV EAX,[EAX+SSS.SegmPtr] ; Get curent segment. TEST EAX JZ .30: MOV EDX,[EAX+SSS.Status] ; Parent segment status. AND EDX,sssWidthMask+sssCombineMask+sssNotBSS ; New section inherits those properties. OR EDX,sssSection+sssDefinedInPass Invoke SssCreateSe::,EBX,EAX,ESI,ECX,EDX,[EAX+SSS.Purpose],[EAX+SSS.Alignment] ; Returns pointer to the new section in EAX. RstSt [EAX+SSS.Purpose],sssPurposeLITERAL ; This purpose is not inherited. JMP .40: .30: ; No previous segment was specified, let's create a new segment+section with the name ESI,ECX. MOV EDX,pgmoptWidthMask AND EDX,[EDI+PGM.Pgmopt.Status] ; Inherit segment width from program width. SetSt EDX,sssSegment+sssSection+sssPublic+sssDefinedInPass Invoke SssCreateSe::,EBX,0,ESI,ECX,EDX,0,16 Invoke SssGuessPurpose::,EAX JNZ .40: SetSt [EAX+SSS.Purpose],sssPurposeRegular .40: MOV [EBX+STM.Section],EAX ; Switch to section EAX. MOV [EDI+PGM.CurrentSect],EAX SetSt [Src.Lst.Status::],lstSectInline ; Display [NewSect] in dump column on the same line of listing. JNSt [EBX+STM.Status],stmOperationPresent,.90: LEA ECX,[EBX+STM.OperationPtr] Msg '6860',ECX ; Unrecognized operation "!1S", ignored. JMP .90: .50: ; Label is missing or it is not in [], e.g.: Unknown
. JNSt [EBX+STM.Status],stmOperationPresent,.55: LEA ECX,[EBX+STM.OperationPtr] Msg '6860',ECX ; Unrecognized operation "!1S", ignored. JMPS .60: .55:; Statement EBX may have label/prefix but it is not [section] switch. E.g.Label:LOCK
. Invoke StmCheckFields::,EBX,"**00" JC .90: .60: MOVB [EBX+STM.Status],'A' MOV EDX,[EDI+PGM.CurrentSect] TEST EDX JNZ .65: Invoke SssGetSegm::,EDI, sssPurposeCODE MOV EDX,EAX .65: MOV ECX,[EBX+STM.NrOfPrefixes] MOV [EBX+STM.Size],ECX JECXZ .80: SetSt [EBX+STM.Flags],stmtNotBSS ; The statement has machine prefix(es).. MOVB [EBX+STM.Status],'I' StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. LEA ESI,[EBX+STM.Prefix1Data] .70:BufferStore [EBX+STM.EmitBuffer],ESI,1 ADD ESI,STM.Prefix2Data-STM.Prefix1Data LOOP .70: JNSt [Ea.Eaopt.Status::],eaoptAUTOSEGMENT, .80: MOV EDX,[EDI+PGM.CurrentSect] TEST EDX JZ .75: JSt [EDX+SSS.Purpose],sssPurposeCODE,.80: .75: Invoke SssGetSegm::, EDI, sssPurposeCODE ; Find the first code section to swich to when AUTOSEGMENT=ON. JC .80: MOV EDX,EAX .80: MOV [EBX+STM.Section],EDX MOV ESI,EDX TEST ESI JZ .90: MOV EAX,[ESI+SSS.OrgLow] ; Update statement offset when section changed. MOV EDX,[ESI+SSS.OrgHigh] MOV [EBX+STM.OffsetLow],EAX MOV [EBX+STM.OffsetHigh],EDX JNSt [EBX+STM.Status],stmLabelPresent,.90: ; The statement has a label. MOV EAX,stmLabelIsPublic AND EAX,[EBX+STM.Status] OR EAX,symDefined Invoke SymCreate::,EAX,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EBX .90:EndProcedure PseudoNoOperation
PseudopcCOMMENT Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData MOV ECX,[EBX+STM.CtxStatusAll] AND ECX,ctxNoEmit JNZ .50: Invoke StmCheckFields::,EBX,"?000" .50:StackPush [Src.CtxStack::],0 OR ECX,ctxCOMMENT+ctxNoEmit Invoke CtxCreate::,EAX,ECX,EBX .90:EndProcedure PseudopcCOMMENT
PseudopcENDCOMMENT Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData Invoke CtxFind::,ctxCOMMENT,EBX Msg cc=C,'7120',Dict_PseudopcENDCOMMENT:: ; Wrong nesting, unexpected !1S. JC .90: Msg cc=NE,'7110',Dict_PseudopcENDCOMMENT::,EAX ; Wrong nesting, expected "!1S !2S". .50:Invoke StmCheckFields::,EBX,"00?0" Invoke CtxDiscard::,EAX,EBX .90:EndProcedure PseudopcENDCOMMENT
PseudopcDISPLAY Procedure Stm ContBuf LocalVar ; Buffer for contents of expanded %variable. SortBuf LocalVar ; Buffer contains pointers to object name. Used for aplhabetical sort. StrBuf LocalVar ; Buffer contains Qwords(ptr,size) with object name. NameBuf LocalVar ; Buffer contains concatenated % and formal name of variables. PgmPtr LocalVar ; Cached pointer to the current PGM object. ParA LocalVar ; Temporary storage for Msg parameters. ParB LocalVar ; Temporary storage for Msg parameters. ParC LocalVar ; Temporary storage for Msg parameters. ParD LocalVar ; Temporary storage for Msg parameters. ParR LocalVar Size=24 ; File range, such as "{1234567890..1234567890}" ParQN LocalVar Size=8 ; Qword(ptr,size) with raw key name. ParQV LocalVar Size=8 ; Qword(ptr,size) with raw key value. MaskQV LocalVar Size=8 ; Qword(ptr,size) with normalized key value (filterring mask). ParVal LocalVar Size=24 ; Room for parameter conversion to a decimal number. VarName LocalVar Size=32 ; Maximal length of any Pgmopt/Eaopt/SysVar name used in €ASM is 32. MOV EBX,[%Stm] Invoke StmCheckFields::,EBX,"00**" JC .99: ; Initialization of %DISPLAY engine. SetSt [Src.Lst.Status::],lstNoData Invoke EaBufferReserve::,PseudopcDISPLAY MOV [%ContBuf],EAX Invoke EaBufferReserve::,PseudopcDISPLAY MOV [%SortBuf],EAX Invoke EaBufferReserve::,PseudopcDISPLAY MOV [%StrBuf],EAX Invoke EaBufferReserve::,PseudopcDISPLAY MOV [%NameBuf],EAX MOV EAX,[EBX+STM.Program] MOV [%PgmPtr],EAX ; Processing %DISPLAY ordinal operands. LEA ECX,[%MaskQV] XOR EAX,EAX MOV [ECX+0],EAX ; Ordinal %DISPLAY operands have no filterring mask. MOV [ECX+4],EAX MOV ECX,[EBX+STM.NrOfOrdinals] JECXZ .20: BufferRetrieve [EBX+STM.OrdBuffer] MOV ECX,[EBX+STM.NrOfOrdinals] .10: MOV EDI,ESI ; EDI points to Qword(ptr,size) of ordinal operand. SUB EDX,EDX PUSH EBX,ECX,ESI CALL .Operand ; EDI=^Qword(ptr,size) with %DISPLAY key, EDX=0 (no filter). POP ESI,ECX,EBX ADD ESI,8 LOOP .10: .20:; Processing %DISPLAY keyword operands. It may specify a mask, e.g. %DISPLAY Sym=Sys* BufferRetrieve [EBX+STM.KeyBuffer] MOV ECX,[EBX+STM.NrOfKeys] JECXZ .40: .30: MOV EDI,ESI ; EDI points to Oword(nameptr,namesize,valueptr,valuesize) od keyword operand. LEA EDX,[ESI+8] PUSH ECX,ESI CALL .Operand ; EDI=^Qword with %DISPLAY key, EDX=^Qword with raw filter value. POP ESI,ECX ADD ESI,16 LOOP .30: .40: Msg '1790' ; **** End of %%DISPLAY Invoke EaBufferRelease::,[%NameBuf] Invoke EaBufferRelease::,[%StrBuf] Invoke EaBufferRelease::,[%SortBuf] Invoke EaBufferRelease::,[%ContBuf] .99:EndProcedure PseudopcDISPLAY ; %DISPLAY handlers will emit one debug message for each €ASM object. ; Input: EDI=^QW (ptr,size) with key category name (e.g. "Literals") ; EBX=[%PgmPtr]. ; [%MaskQV]=^QW(ptr,size) with normalized filter value, perhaps empty. ; Output:Handlers may destroy any GPR but EBP. PseudopcDISPLAY.ALL::PROC ; Display all €ASM objects. LEA EDX,[%MaskQV] Msg '1100',EDX ; **** %%DISPLAY All=!1S* CALL PseudopcDISPLAY.FILES CALL PseudopcDISPLAY.CHUNKS CALL PseudopcDISPLAY.SEGMENTS CALL PseudopcDISPLAY.STRUCTURES CALL PseudopcDISPLAY.CONTEXT CALL PseudopcDISPLAY.SYMBOLS CALL PseudopcDISPLAY.LITERALSYMBOLS CALL PseudopcDISPLAY.RELOCATIONS CALL PseudopcDISPLAY.MACROS CALL PseudopcDISPLAY.VARIABLES RET ENDP PseudopcDISPLAY.ALL:: PseudopcDISPLAY.FILES:: PROC ; Display the name, property and contents of each source/configuration file. Msg '1150' ; **** %%DISPLAY Files MOV ESI,Src.IniFile:: ; Local configuration file. MOV EDX,=B"config" JSt [ESI+FILE.Status],fileStFound,.F1: MOV EDX,=B"not found" .F1: LEA EDI,[ESI+FILE.Name] Msg '1160',EDI,[ESI+FILE.Size],EDX ; "!1$",size=!2K,src=!3$. MOV ESI,Ea.SrcFile:: ; Main source file. LEA EDI,[ESI+FILE.Name] Msg '1170',EDI,[ESI+FILE.Size] ; "!1$",size=!2K,src=main source SUB ECX,ECX ; Included files counter. .F3: CMP ECX,[EBX+PGM.InclFilesNr] JNB .F9: MOV ESI,[EBX+PGM.InclLinePtrTable] MOV EDI,[EBX+PGM.InclFilesTable] MOV EDX,[ESI+4*ECX] ; EDX=LinePtr MOV ESI,[EDI+4*ECX] ; ESI=^FILE. LEA EAX,[ESI+FILE.Name] Msg '1180',EAX,[ESI+FILE.Size],EDX ; "!1$",size=!2K,src=included in !3@. INC ECX JMP .F3: .F9: RET ENDP PseudopcDISPLAY.FILES:: PseudopcDISPLAY.CHUNKS:: PROC Msg '1200' ; **** %%DISPLAY Chunks of source text. ListGetFirst [Src.ChunkList::] JZ .I9: .I0:MOV EBX,[EAX+CHUNK.FilePtr] SUB ESI,ESI MOV [%ParA],ESI TEST EBX JZ .I1: CALL .ChunkRange LEA ESI,[EBX+FILE.Name] .I1:LEA EBX,[%ParQN] MOV EDI,[EAX+CHUNK.Bottom] MOV EDX,[EAX+CHUNK.Top] SUB EDX,EDI MOV ECX,EDX StripSpaces EDI,EDX ; Display only non-white chunk contents. MOV [EBX+0],EDI MOV [EBX+4],EDX JNSt [EAX+CHUNK.Status],chunkError,.I3: LEA EDI,[%ParVal] MOV EDX,[EAX+CHUNK.Status] SHR EDX,16 ; DX is now binary Msg identifier. PUSH EAX,EDI MOV AL,'W' CMP DX,5000 JB .I2: MOV AL,'E' .I2: STOSB MOV EAX,EDX StoD EDI,Size=4,Align=right,LeadingZeroes=yes XOR EAX,EAX STOSB POP EDI,EAX JMP .I5: .I3:MOV EDI,=B"orig" JSt [EAX+CHUNK.Status],chunkOrig|chunkOrigBin,.I5: MOV EDI,=B"binary" JSt [EAX+CHUNK.Status],chunkBin,.I5: MOV EDI,=B"source" JNSt [EAX+CHUNK.Status],chunkResolved|chunkSkipped,.I5: MOV EDI,=B"resolved" JNSt [EAX+CHUNK.Status],chunkSkipped,.I5: MOV EDI,=B"skipped" .I5:MOV EDX,=B"included" JSt [EAX+CHUNK.Status],chunkIncluded,.I8: MOV EDX,=B"envelope" JSt [EAX+CHUNK.Status],chunkEnvelope,.I8: MOV EDX,=B"main" .I8:Msg '1210',ESI,[%ParA],EDX,EDI,ECX,EBX ; !"!1$"!2S,src=!3$,type=!4$,size=!5D,contents=''!6S'' ListGetNext EAX JNZ .I0: .I9:RET ENDP PseudopcDISPLAY.CHUNKS:: PseudopcDISPLAY.CHUNKS.ChunkRange PROC ; Create chunk range within the file contents as sublist operation. ; Input: EAX=^CHUNK, EBX=^FILE. ; Output: %ParR= sublist operator which describes the chunk, e.g. "{12..23}" ; %ParQV= Dword(ptr,size) of the sublist operator in %ParR. ; %ParA= ^ParQV (Msg parameter !2S). ECX,EDX,ESI,EDI undefined. PUSH EAX MOV EDI,[EBX+FILE.BufPtr] MOV ECX,[EAX+CHUNK.Bottom] MOV EAX,[EBX+FILE.BufSize] SUB EDX,EDX ; Line counter. ADD EAX,EDI ; BufTop. CMP ECX,EAX JNB .90: ; If chunk EAX is not from file EBX, leave %ParA=0. SUB ECX,EDI JB .90: ; If chunk EAX is not from file EBX, leave %ParA=0. ; Examine left range value. MOV AL,10 INC EDX ; Line numbers start from 1. .20:JECXZ .30: REPNE SCASB JNE .20: INC EDX JMP .20: .30:LEA EDI,[%ParR] MOV AL,'{' STOSB XCHG EAX,EDX MOV [%ParC],EAX ; Temporary store left range value. StoD EDI MOV [%ParB],EDI ; Temporary save ptr to future range operator .. into ParR. POP EAX ; Examine right range value. PUSH EAX MOV EDI,[EBX+FILE.BufPtr] MOV ECX,[EAX+CHUNK.Top] SUB EDX,EDX ; Line counter. SUB ECX,EDI MOV AL,10 INC EDX ; Line numbers start from 1. .40:JECXZ .50: REPNE SCASB JNE .40: INC EDX JMP .40: .50:CMP [EDI-1],AL JNE .60: DEC EDX .60:MOV EDI,[%ParB] CMP EDX,[%ParC] JBE .80: ; If left and right range values are equal or if range is empty. MOV AL,'.' STOSB ; Range operator. STOSB XCHG EAX,EDX StoD EDI ; Right range value. .80:MOV AL,'}' STOSB LEA ESI,[%ParR] SUB EDI,ESI LEA EAX,[%ParQV] MOV [%ParA],EAX MOV [EAX+0],ESI MOV [EAX+4],EDI .90:POP EAX RET ENDP PseudopcDISPLAY.CHUNKS.ChunkRange PseudopcDISPLAY.GROUPS:: PseudopcDISPLAY.SECTIONS:: PseudopcDISPLAY.SEGMENTS:: PROC Msg '1250' ; **** %%DISPLAY groups, segments and sections. MOV EBX,[%PgmPtr] TEST EBX JZ .Se9: ListGetFirst [EBX+PGM.SssList] JZ .Se9: .Se1:JNSt [EAX+SSS.Status],sssGroup,.Se3: CALL .Gr: ; %Display group EAX and its segments. .Se3:ListGetNext EAX JNZ .Se1: ListGetFirst [EBX+PGM.SssList] ; %Display groupless segments. .Se5:JNSt [EAX+SSS.Status],sssSegment,.Se8: CMPD [EAX+SSS.GroupPtr],0 JNZ .Se8: ; If segment EAX belongs to a group, it was already displayed. CALL .Sg: ; %Display groupless segment EAX and its sections. .Se8:ListGetNext EAX JNZ .Se5: .Se9:RET .Gr: PUSHAD ; %Display group EAX and its segments. MOV EDX,EAX BufferClear [%NameBuf] BufferClear [%SortBuf] ListGetFirst [EBX+PGM.SssList] .Gr1:JNSt [EAX+SSS.Status],sssSegment,.Gr2: CMP [EAX+SSS.GroupPtr],EDX JNE .Gr2: PUSH EAX MOV EAX,ESP BufferStore [%SortBuf],EAX,4 POP EAX .Gr2:ListGetNext EAX JNZ .Gr1: BufferRetrieve [%SortBuf] SAR ECX,2 JZ .Gr5: .Gr3:LODSD BufferStore [%NameBuf],=B' [',2 BufferStore [%NameBuf],[EAX+0],[EAX+4] BufferStore [%NameBuf],=B']',1 LOOP .Gr3: .Gr5:BufferRetrieve [%NameBuf] PUSH ECX,ESI MOV ECX,ESP Msg '1260',EDX,ECX,[EDX+SSS.LinePtr] ; [!1S],group!2S,src=!3@ POP ESI,ECX ListGetFirst [EBX+PGM.SssList] .Gr6:JNSt [EAX+SSS.Status],sssSegment,.Gr8: CMP [EAX+SSS.GroupPtr],EDX JNE .Gr8: CALL .Sg: ; %Display segment EAX and its sections. .Gr8:ListGetNext EAX JNZ .Gr6: POPAD RET .Sg: PUSHAD ; %Display segment EAX and its sections. BufferClear [%SortBuf] BufferClear [%NameBuf] Invoke SssPurposeToText::,[EAX+SSS.Purpose],[%NameBuf] BufferRetrieve [%NameBuf] MOV [%ParA],ESI MOV EBX,sssWidthMask MOV EDX,[EAX+SSS.Status] AND EBX,EDX SAR EBX,20 MOV [%ParB],EBX MOV EBX,[EAX+SSS.Alignment] MOV [%ParC],EBX MOV EBX,sssCombineMask AND EBX,EDX Invoke DictSearchByData::, DictSegmentCombine::,EBX LEA ECX,[EAX+SSS.ClassPtr] Msg '1270',EAX,[%ParA],[%ParB],[%ParC],ESI,ECX,[EAX+SSS.LinePtr] ; [!1S],purpose=!2$,width=!3D,align=!4K,combine=!5S,class="!6S",source=!7@ ; Now display all sections which belong to segment EAX. The first section is always identical with segment EAX. MOV EDX,[%PgmPtr] MOV EBX,EAX ListGetFirst [EDX+PGM.SssList] .Sg3:JNSt [EAX+SSS.Status],sssSection, .Sg6: CMP EBX,[EAX+SSS.SegmPtr] JNE .Sg6: ; If not our segment's section. PUSH EAX MOV EAX,ESP BufferStore [%SortBuf],EAX,4 ; Store pointer to section. POP EAX .Sg6:ListGetNext EAX JNZ .Sg3: BufferRetrieve [%SortBuf] ; Sections will be displayed in SECTION# order, i.e. by bottom offset. SAR ECX,2 JZ .Sg9: ShellSort ESI,ECX,4, .CmpSssBottom BufferRetrieve [%SortBuf] SAR ECX,2 .Sg7:LODSD MOV EDX,[EAX+SSS.TopLow] MOV EBX,[EAX+SSS.Alignment] SUB EDX,[EAX+SSS.BottomLow] PUSH ECX MOV CX,"YN" JSt [EAX+SSS.Status],sssUsed,.Sg8: XCHG CL,CH .Sg8:Msg '1280',EAX,[EAX+SSS.BottomLow],EDX,EBX,ECX,[EAX+SSS.LinePtr] ; !1S],address=!2Hh,size=!3Hh=!3K,align=!4D,ref=!5Z,src=!6@ POP ECX LOOP .Sg7: .Sg9:POPAD RET ENDP PseudopcDISPLAY.SEGMENTS:: PseudopcDISPLAY.SEGMENTS.CmpSssBottom PROC ; Callback to compare two sections pointed to with [ESI] and [EDI] by .Bottom. PUSH EBX ; ShellSort requires to keep EBX,ECX unchanged. MOV EDX,[EDI] MOV EBX,[ESI] MOV EAX,[EDX+SSS.BottomLow] CMP EAX,[EBX+SSS.BottomLow] JAE .90: ; If the order is ascending. MOV [ESI],EDX ; Otherwise swap both pointers and set CF. MOV [EDI],EBX STC .90:POP EBX RET ENDP PseudopcDISPLAY.SEGMENTS.CmpSssBottom PseudopcDISPLAY.STRUCTURES::PROC LEA EDX,[%MaskQV] Msg '1300',EDX ; **** %%DISPLAY Structures=!1S* BufferClear [%SortBuf] MOV EBX,[%PgmPtr] TEST EBX JZ .St9 ListGetFirst [EBX+PGM.SssList] JZ .St9: .St2:JNSt [EAX+SSS.Status],sssStructure, .St8: CALL PseudopcDISPLAY.PutObj .St8:ListGetNext EAX JNZ .St2: Invoke EaBufferSort::,[%SortBuf] BufferRetrieve [%SortBuf] SAR ECX,2 JZ .St9: ; If empty buffer. .DS1:LODSD PUSH ECX,ESI BufferClear [%NameBuf] Invoke SssPurposeToText::,[EAX+SSS.Purpose],[%NameBuf] BufferRetrieve [%NameBuf] MOV [%ParA],ESI MOV ECX,[EAX+SSS.Status] MOV BX,"YN" JSt ECX,sssUsed,.DS5: XCHG BL,BH .DS5: MOV ECX,[EAX+SSS.Alignment] ; !1S,size=!2K,align=!3K,purpose=!4$,ref=!5Z,source=!6@ Msg '1310',EAX,[EAX+SSS.TopLow],ECX,[%ParA],EBX,[EAX+SSS.LinePtr] POP ESI,ECX LOOP .DS1: .St9:RET ENDP PseudopcDISPLAY.STRUCTURES:: PseudopcDISPLAY.CONTEXT:: PROC Msg '1350' ; **** %%DISPLAY Context MOV EDI,[Src.CtxStack] TEST EDI JZ .Ctx9: MOV EBX,[EDI+STACK.Bottom] .Ctx1:CMP EBX,[EDI+STACK.Ptr] JNB .Ctx9: MOV EAX,[EBX+CTX.Status] ; EBX=context EDI=Src.CtxStack MOV ESI,Dict_PseudoPROGRAM:: JSt EAX,ctxPROGRAM,.Ctx3: MOV ESI,Dict_PseudoHEAD:: JSt EAX,ctxHEAD,.Ctx3: MOV ESI,Dict_PseudoPROC:: JSt EAX,ctxPROC,.Ctx3: MOV ESI,Dict_PseudoPROC1:: JSt EAX,ctxPROC1,.Ctx3: MOV ESI,Dict_PseudoSTRUC:: JSt EAX,ctxSTRUC,.Ctx3: MOV ESI,Dict_PseudopcMACRO:: JSt EAX,ctxMACRO,.Ctx3: MOV ESI,Dict_PseudopcFOR:: JSt EAX,ctxFOR,.Ctx3: MOV ESI,Dict_PseudopcWHILE:: JSt EAX,ctxWHILE,.Ctx3: MOV ESI,Dict_PseudopcREPEAT:: JSt EAX,ctxREPEAT,.Ctx3: MOV ESI,Dict_PseudopcIF:: JSt EAX,ctxIF,.Ctx3: MOV ESI,Dict_PseudopcCOMMENT:: JSt EAX,ctxCOMMENT,.Ctx3: SUB ESI,ESI .Ctx3:MOV EDX,=B"envelope" JNSt EAX,ctxPROGRAM,.Ctx4: MOV ECX,[EBX+CTX.ObjPtr] JECXZ .Ctx4: JSt [ECX+PGM.Status],pgmEnvelope,.Ctx5: .Ctx4:MOV EDX,=B"main" .Ctx5:AND EAX,ctxNoEmit MOV AX,"YN" JZ .Ctx6: XCHG AL,AH .Ctx6:Msg '1360',EBX,ESI,EDX,EAX,[EBX+CTX.ExpansionNr] ; !1S !2S,src=!3$,emit=!4Z,%%.=!5D ADD EBX,SIZE#CTX JMP .Ctx1: .Ctx9: RET ENDP PseudopcDISPLAY.CONTEXT:: PseudopcDISPLAY.DisplaySym PROC ; Common procedure to display all symbols whose pointers are in [%SortBuf]. Invoke EaBufferReserve::,PseudopcDISPLAY.DisplaySym MOV [%ParB],EAX Invoke EaBufferSort::,[%SortBuf] ; Order displayed symbols alphabetically. BufferRetrieve [%SortBuf] SAR ECX,2 JZ .DS9: ; If empty buffer. .DS1:LODSD ; EAX is now pointer to the displayed symbol. PUSH EBX,ECX,ESI ; Prepare Msg parameters !7Z,!8Z,!6$ to BL,CL,DL. MOV BX,"YN" JSt [EAX+SYM.Status],symFixed,.DS4: XCHG BL,BH .DS4: MOV CX,"YN" JSt [EAX+SYM.Status],symReferenced,.DS5: XCHG CL,CH .DS5: MOV DL,'X' JSt [EAX+SYM.Status],symExport, .DS6: MOV DL,'I' JSt [EAX+SYM.Status],symImport, .DS6: MOV DL,'P' JSt [EAX+SYM.Status],symPublic, .DS6: MOV DL,'E' JSt [EAX+SYM.Status],symExtern, .DS6: MOV DL,'G' JSt [EAX+SYM.Status],symGlobal|symGlobalRef, .DS6: MOV DL,'S' .DS6: BufferClear [%ParB] MOV DH,"'" BufferStoreWord [%ParB],EDX CMPD [EAX+SYM.DllNameSize],0 JZ .DS8: JSt [EAX+SYM.Status],symDefined,.DS8: ; Do not display lib= when public symbol is exported. BufferStoreDword [%ParB],',lib' BufferStoreWord [%ParB],'="' BufferStore [%ParB],[EAX+SYM.DllNamePtr],[EAX+SYM.DllNameSize] .DS7: BufferStoreByte [%ParB],'"' .DS8: BufferStoreByte [%ParB],0 PUSH ECX BufferRetrieve [%ParB] POP ECX Msg '1450',EAX,[EAX+SYM.Section],[EAX+SYM.OffsetLow],[EAX+SYM.Status], \ [EAX+SYM.Size],ESI,ECX,EBX,[EAX+SYM.LinePtr] ; !1S,[!2S]:!3Hh,type=''!4Z'',size=!5K,scope=''!6$,ref=''!7Z'',fix=!8Z,source=!9@ POP ESI,ECX,EBX DEC ECX JNZ .DS1: .DS9:Invoke EaBufferRelease::,[%ParB] RET ENDP PseudopcDISPLAY.DisplaySym PseudopcDISPLAY.SYMBOLS:: PROC ; Display all non-literal symbols (fixed, unfixed, referenced, unreferenced). LEA EDX,[%MaskQV] Msg '1400',EDX ; **** %%DISPLAY Symbols=!1S* BufferClear [%SortBuf] MOV EBX,[%PgmPtr] TEST EBX JZ .Su9: ListGetFirst [EBX+PGM.SymList] JZ .Su9: .Su2: JSt [EAX+SYM.Status],symLiteral, .Su8: CALL PseudopcDISPLAY.PutObj .Su8: ListGetNext EAX JNZ .Su2: CALL PseudopcDISPLAY.DisplaySym .Su9: RET ENDP PseudopcDISPLAY.SYMBOLS:: PseudopcDISPLAY.UNFIXEDSYMBOLS::PROC LEA EDX,[%MaskQV] Msg '1410',EDX ; **** %%DISPLAY UnfixedSymbols=!1S* BufferClear [%SortBuf] MOV EBX,[%PgmPtr] TEST EBX JZ .Su9: ListGetFirst [EBX+PGM.SymList] JZ .Su9: .Su2:;;;;;;;;;;; JSt [EAX+SYM.Status],symLiteral, .Su8: JSt [EAX+SYM.Status],symFixed, .Su8: CALL PseudopcDISPLAY.PutObj .Su8: ListGetNext EAX JNZ .Su2: CALL PseudopcDISPLAY.DisplaySym .Su9: RET ENDP PseudopcDISPLAY.UNFIXEDSYMBOLS:: PseudopcDISPLAY.FIXEDSYMBOLS::PROC LEA EDX,[%MaskQV] Msg '1420',EDX ; **** %%DISPLAY FixedSymbols=!1S* BufferClear [%SortBuf] MOV EBX,[%PgmPtr] TEST EBX JZ .Sr9: ListGetFirst [EBX+PGM.SymList] JZ .Sr9: .Sr2: JSt [EAX+SYM.Status],symLiteral,.Sr8: JNSt [EAX+SYM.Status],symFixed, .Sr8: CALL PseudopcDISPLAY.PutObj .Sr8: ListGetNext EAX JNZ .Sr2: CALL PseudopcDISPLAY.DisplaySym .Sr9: RET ENDP PseudopcDISPLAY.FIXEDSYMBOLS:: PseudopcDISPLAY.UNREFERENCEDSYMBOLS::PROC LEA EDX,[%MaskQV] Msg '1430',EDX ; **** %%DISPLAY UnreferencedSymbols=!1S* BufferClear [%SortBuf] MOV EBX,[%PgmPtr] TEST EBX JZ .Su9: ListGetFirst [EBX+PGM.SymList] JZ .Su9: .Su2: ;;;;;;;;JSt [EAX+SYM.Status],symLiteral, .Su8: JSt [EAX+SYM.Status],symUsed, .Su8: CALL PseudopcDISPLAY.PutObj .Su8: ListGetNext EAX JNZ .Su2: CALL PseudopcDISPLAY.DisplaySym .Su9: RET ENDP PseudopcDISPLAY.UNREFERENCEDSYMBOLS:: PseudopcDISPLAY.REFERENCEDSYMBOLS::PROC LEA EDX,[%MaskQV] Msg '1440',EDX ; **** %%DISPLAY ReferencedSymbols=!1S* BufferClear [%SortBuf] MOV EBX,[%PgmPtr] TEST EBX JZ .Sr9: ListGetFirst [EBX+PGM.SymList] JZ .Sr9: .Sr2: JSt [EAX+SYM.Status],symLiteral,.Sr8: JNSt [EAX+SYM.Status],symUsed, .Sr8: CALL PseudopcDISPLAY.PutObj .Sr8: ListGetNext EAX JNZ .Sr2: CALL PseudopcDISPLAY.DisplaySym .Sr9: RET ENDP PseudopcDISPLAY.REFERENCEDSYMBOLS:: PseudopcDISPLAY.LITERALSYMBOLS::PROC LEA EDX,[%MaskQV] Msg '1500',EDX ; **** %%DISPLAY LiteralSymbols=!1S* BufferClear [%SortBuf] MOV EBX,[%PgmPtr] TEST EBX JZ .Sl9: ListGetFirst [EBX+PGM.SymList] JZ .Sl9: .Sl2: JNSt [EAX+SYM.Status],symLiteral, .Sl8: CALL PseudopcDISPLAY.PutObj .Sl8: ListGetNext EAX JNZ .Sl2: CALL PseudopcDISPLAY.DisplaySym .Sl9: RET ENDP PseudopcDISPLAY.LITERALSYMBOLS:: PseudopcDISPLAY.RELOCATIONS:: PROC Msg '1550' ; **** %%DISPLAY relocations ListGetFirst [EBX+PGM.SssList] JZ .RL9: .RL2:MOV [%ParA],EAX ; ^SSS. BufferRetrieve [EAX+SSS.EmitBuffer] MOV [%ParB],ESI ADD ECX,ESI MOV [%ParC],ECX ; Emited contents is between [%ParB]..[%ParC]. BufferRetrieve [EAX+SSS.RelocBuffer] JECXZ .RL8: ADD ECX,ESI MOV [%ParD],ECX .RL3:CALL .RelocESI: ; Display one RELOC ESI. ADD ESI,SIZE# RELOC CMP ESI,[%ParD] JB .RL3: ; The next RELOC. .RL8:ListGetNext [%ParA] ; The next segment. JNZ .RL2: .RL9:RET ENDP PseudopcDISPLAY.RELOCATIONS:: PseudopcDISPLAY.RELOCATIONS.RelocESI: PROC ; Display one relocation at ESI. ; EBX EDI ; 1560 at=[!1S]:!2Hh,width=16,obj=!3Wh,add=!4Wh,type=!5$,tg=!6S,[!7S]:!8Hh ; 1570 at=[!1S]:!2Hh,width=32,obj=!3Hh,add=!4Hh,type=!5$,tg=!6S,[!7S]:!8Hh ; 1580 at=[!1S]:!2Hh,width=64,obj=!3Qh,add=!4Qh,type=!5$,tg=!6S,[!7S]:!8Hh MOV EDX,[ESI+RELOC.Status] JSt EDX,relocIgnore,.RE9: XOR EBX,EBX ; EBX will be the value of relocated object in emitted contents (or pointer if 64). MOV EDI,[ESI+RELOC.OrgLow] MOV ECX,[ESI+RELOC.Section] SUB EDI,[ECX+SSS.BottomLow] ADD EDI,[%ParB] ; Let EDI point to the relocated object. INC EDI,EDI CMP EDI,[%ParC] JNB .RE1: DEC EDI,EDI MOV EBX,EDI JSt EDX,relocWidth64,.RE1: MOV EBX,[EDI] .RE1: MOV EDI,=B"abs" ; EDI will be the relocation type. JSt EDX,relocAbsVA,.RE3: MOV EDI,=B"rel" JSt EDX,relocRel,.RE3: MOV EDI,=B"absRVA" JSt EDX,relocAbsRVA,.RE3: MOV EDI,=B"para" JSt EDX,relocPara,.RE3: MOV EDI,=B"far" JSt EDX,relocFar,.RE3: MOV EDI,=B"?" .RE3: MOV ECX,[ESI+RELOC.Symbol] XOR EAX,EAX XOR EDX,EDX JECXZ .RE5: MOV EAX,[ECX+SYM.Section] MOV EDX,[ECX+SYM.OffsetLow] .RE5: JNSt [ESI+RELOC.Status],relocWidth32,.RE6: Msg '1570',[ESI+RELOC.Section],[ESI+RELOC.OrgLow],EBX,[ESI+RELOC.AddendLow],EDI,ECX,EAX,EDX RET .RE6: JNSt [ESI+RELOC.Status],relocWidth64,.RE7: PUSH EBP LEA EBP,[ESI+RELOC.AddendLow] Msg '1580',[ESI+RELOC.Section],[ESI+RELOC.OrgLow],EBX,EBP,EDI,ECX,EAX,EDX POP EBP RET .RE7: Msg '1560',[ESI+RELOC.Section],[ESI+RELOC.OrgLow],EBX,[ESI+RELOC.AddendLow],EDI,ECX,EAX,EDX .RE9: RET ENDP PseudopcDISPLAY.RELOCATIONS.RelocESI: PseudopcDISPLAY.MACROS:: PROC LEA EDX,[%MaskQV] Msg '1600',EDX ; **** %%DISPLAY Macros=!1S* BufferClear [%SortBuf] MOVD [%ParA],0 .M1:Invoke CtxPeek::, ctxPROGRAM, [%ParA] JC .M5: MOV [%ParA],EAX ; PROGRAM context. MOV EBX,[EAX+CTX.ObjPtr] TEST EBX JZ .M1: MOV ECX,[EBX+PGM.PassPtr] JECXZ .M1: ListGetFirst [ECX+PASS.MacList] JZ .M1: .M2:CALL PseudopcDISPLAY.PutObj ListGetNext EAX JNZ .M2: JMP .M1: .M5:Invoke EaBufferSort::,[%SortBuf] BufferRetrieve [%SortBuf] SAR ECX,2 JZ .M9: .M7:LODSD MOV EBX,=B'default' JNSt [EAX+MAC.Status],macLabeled,.M8: MOV EBX,=B'%%:' .M8:Msg '1610',EAX,EBX,[EAX+MAC.LinePtr] ; !1S,entry=!2$,source=!3@ LOOP .M7: .M9:RET ENDP PseudopcDISPLAY.MACROS:: PseudopcDISPLAY.DisplayVar PROC ; Common procedure for display (as message D1770/D1780) ; preprocessing %variables whose pointers are stored in %SortBuf. ; Input: EDX=0 only if system %^variables are presented. Nonzero in automatic, formal and user-defined %variables. Invoke EaBufferSort::,[%SortBuf] BufferRetrieve [%SortBuf] SAR ECX,2 JZ .DV9: ; If empty buffer. .DV1: BufferClear [%ContBuf] ; Temporary buffer for %variable contents. LODSD ; EAX is now ^VAR. TEST EDX JNZ .DV4: ; Use D1770 if formal or user-defined %variable is displayed. MOV EDI,'1780' ; Volatile system variables %^DATE,%^TIME,%^TIMESTAMP,%^EUROASMOS,%^VERSION ; are presented with MsgId='1780' instead of '1770' in order to allow suppressing with NOWARN=1780 in tests. MOV EBX,[EAX+0] MOV EBX,[EBX+2] ; Skip the first two characters %^ of system variable name. CMP EBX,"TIME" JE .DV5: CMP EBX,"DATE" JE .DV5: CMP EBX,"EURO" JE .DV5: CMP EBX,"VERS" JE .DV5: .DV4: MOV EDI,'1770' ; Default MsgId. .DV5: MOV EBX,[EAX+4] ; Variable name size, including the leading percent sign. ADD EBX,[EAX+0] Invoke VarExpand::,[EAX+0],EBX,[%ContBuf],-1 ; Get %variable contents into %ContBuf. JC .DV8: MOV EAX,[ESI-4] ; Restore pointer to VAR. PUSH ECX,ESI BufferRetrieve [%ContBuf] PUSH ECX,ESI ; Size,Ptr to %variable contents. MOV EBX,ESP ; EBX=Qword(Ptr,size) with %variable contents. Msg EDI,EAX,EBX,ECX ; !1S=!2S, size=!3D. POP ESI,ECX POP ESI,ECX ; Restore pointer in %SortBuf contents. .DV8: LOOP .DV1: ; Display the next %variable. .DV9: RET ENDP PseudopcDISPLAY.DisplayVar PseudopcDISPLAY.VARIABLES::PROC LEA EDX,[%MaskQV] Msg '1700',EDX ; **** %%DISPLAY Variables=%%!1S* CALL PseudopcDISPLAY.AUTOMATICVARIABLES CALL PseudopcDISPLAY.FORMALVARIABLES CALL PseudopcDISPLAY.USERVARIABLES CALL PseudopcDISPLAY.SYSTEMVARIABLES RET ENDP PseudopcDISPLAY.VARIABLES:: PseudopcDISPLAY.AUTOMATICVARIABLES:: PROC Msg '1710' ; **** %%DISPLAY AutomaticVariables. No filter applies. Invoke CtxPeek::, ctxMACRO,0 JC .Va9: ; If not in macro context. MOV EBX,EAX JNSt [EBX+CTX.Status],ctxExpansion,.Va9: ; Skip if in macro definition. BufferRetrieve [EBX+CTX.ObjBuffer] CMP ECX,SIZE#CTX_MAC JNE .Va0: ; This should never happen. ; varTypeLabel %: LEA EDI,[ESI+CTX_MAC.LabelPtr] Msg '1720',=B':',EDI,[EDI+4] ; name="%%!1$",value="!2S",size=!3D' ; varTypeOrd %0,%1,%2,,, LEA EDI,[ESI+CTX_MAC.MacroNamePtr] ; Macro name %0. Msg '1730',0,EDI,[EDI+4] ; name="%%!1D",value="!2S",size=!3D BufferRetrieve [EBX+CTX.OrdBuffer] ; 2*DDs keeping values of ordinal operands. JECXZ .Va3: LEA EAX,[ESI+ECX] ; EAX=end of ordinal Qwords. .Va0: MOVD [%ParA],1 ; ParA keep ordinal number. .Va1: MOV ECX,[EBX+CTX.Shift] ADD ECX,[%ParA] JNG .Va2: ; If shifted out below zero. LEA EDI,[ESI+8*ECX-8] CMP EDI,EAX JNB .Va3: ; If the last ordinal displayed. Msg '1730',[%ParA],EDI,[EDI+4] ; name="%%!1D",value="!2S",size=!3D .Va2: INCD [%ParA] ; The next ordinal. JMP .Va1: .Va3: ; varTypeOrdLen %# BufferRetrieve [EBX+CTX.OrdBuffer] ; Qword(ptr,size) keeping values of ordinal operands. SAR ECX,3 LEA EDI,[%ParVal] MOV EAX,ECX MOV ECX,EDI StoD EDI SUB EDI,ECX LEA EAX,[%ParQV] MOV [EAX+0],ECX MOV [EAX+4],EDI Msg '1720',=B'#',EAX,EDI ; name="%%!1$",value="!2S",size=!3D ; varTypeOrdList %* Invoke EaBufferReserve::,PseudopcDISPLAY.AUTOMATICVARIABLES MOV EDI,EAX ; Temporary buffer for concatenation of ordinals. BufferRetrieve [EBX+CTX.OrdBuffer] JECXZ .Va5: .Va4: BufferStore EDI,[ESI+0],[ESI+4] ADD ESI,8 SUB ECX,8 JNA .Va5: BufferStore EDI,=B',',1 JMP .Va4: .Va5: BufferRetrieve EDI LEA EAX,[%ParQV] MOV [EAX+0],ESI MOV [EAX+4],ECX Msg '1720',=B'*',EAX,ECX ; %%!1$=!2S, size=!3D Invoke EaBufferRelease::,EDI ; varTypeKeyLen %=# BufferRetrieve [EBX+CTX.KeyBuffer] SAR ECX,4 LEA EDI,[%ParVal] MOV EAX,ECX ; Number of used keywords. MOV ECX,EDI StoD EDI SUB EDI,ECX LEA EAX,[%ParQV] MOV [EAX+0],ECX MOV [EAX+4],EDI Msg '1720',=B'=#',EAX,EDI ; name="%%!1$",value="!2S",size=!3D ; varTypeKeyList %* Invoke EaBufferReserve::,PseudopcDISPLAY.AUTOMATICVARIABLES MOV EDI,EAX ; Temporary buffer for concatenation of keywords. BufferRetrieve [EBX+CTX.KeyBuffer] JECXZ .Va7: .Va6: BufferStore EDI,[ESI+0],[ESI+4] BufferStore EDI,=B'=',1 BufferStore EDI,[ESI+8],[ESI+12] ADD ESI,16 SUB ECX,16 JNA .Va7: BufferStore EDI,=B',',1 JMP .Va6: .Va7: BufferRetrieve EDI LEA EAX,[%ParQV] MOV [EAX+0],ESI MOV [EAX+4],ECX Msg '1720',=B'=*',EAX,ECX ; %%!1$=!2S, size=!3D Invoke EaBufferRelease::,EDI .Va9: RET ENDP PseudopcDISPLAY.AUTOMATICVARIABLES:: PseudopcDISPLAY.FORMALVARIABLES:: PROC .VF:: LEA EDX,[%MaskQV] Msg '1740',EDX ; **** %%DISPLAY FormalVariables=%%!1S* BufferClear [%SortBuf] BufferClear [%StrBuf] BufferClear [%NameBuf] SUB EAX,EAX .Vf1:Invoke CtxPeek::, ctxMACRO | ctxFOR, EAX JC .Vf9: ; If no more macro/for context. BufferRetrieve [EAX+CTX.FrmBuffer] JC .Vf5: JECXZ .Vf5: .Vf3: ; ESI,ECX is one or more formal parameters (NamePtr,NameSize,ValPtr,ValSize). NameSize may be 0. PUSH EAX,ECX,ESI MOV ECX,[ESI+4] ; [ESI],ECX is now formal %variable name size without percent sign. JECXZ .Vf4: BufferNew [%StrBuf],8 ; Room for Ptr/Size. MOV EDI,EAX BufferNew [%NameBuf],1 ; Room for % prefix. MOV [EDI+0],EAX ; Ptr to %. MOVB [EAX],'%%' MOV ESI,[ESI+0] ; Pointer to formal name without %. BufferStore [%NameBuf],ESI,ECX INC ECX MOV [EDI+4],ECX ; Size of formal %variable name. MOV EAX,EDI CALL PseudopcDISPLAY.PutObj .Vf4:POP ESI,ECX,EAX ADD ESI,16 SUB ECX,16 JG .Vf3: .Vf5:JMP .Vf1: ; Search for further context from EAX. .Vf9: ; Formal %variables are displayed, EDX<>0. CALL PseudopcDISPLAY.DisplayVar RET ENDP PseudopcDISPLAY.FORMALVARIABLES:: PseudopcDISPLAY.USERVARIABLES:: PROC LEA EDX,[%MaskQV] Msg '1750',EDX ; **** %%DISPLAY UserVariables=%%!1S* BufferClear [%SortBuf] MOVD [%ParA],0 ; Program context marker. .Vu1: Invoke CtxPeek::, ctxPROGRAM, [%ParA] JC .Vu9: MOV [%ParA],EAX ; PROGRAM context. MOV EBX,[EAX+CTX.ObjPtr] TEST EBX ; Does the context specify a program? JZ .Vu1: ; If not, try the parent program context. MOV ECX,[EBX+PGM.PassPtr] JECXZ .Vu1: ListGetFirst [ECX+PASS.VarList] JZ .Vu1: .Vu2: CALL PseudopcDISPLAY.PutObj ListGetNext EAX JNZ .Vu2: ; Find the next %variable. JMP .Vu1: ; Find the parent program context. .Vu9: ; User-defined %variables are displayed, EDX<>0. CALL PseudopcDISPLAY.DisplayVar RET ENDP PseudopcDISPLAY.USERVARIABLES:: PseudopcDISPLAY.SYSTEMVARIABLES::PROC LEA EDX,[%MaskQV] Msg '1760',EDX ; **** %%DISPLAY SystemVariables=%%!1S* ; Comvert mask to uppercase in situ. MOV ESI,[EDX+0] ; MaskPtr. MOV ECX,[EDX+4] ; MaskSize. JECXZ .Vs4: .Vs1: LODSB CMP AL,'a' JB .Vs3: CMP AL,'z' JA .Vs3: AND AL,~('a'^'A') ; Convert AL to uppercase. MOV [ESI-1],AL .Vs3: LOOP .Vs1: .Vs4: BufferClear [%SortBuf] BufferClear [%StrBuf] BufferClear [%NameBuf] MOV EAX,DictPgmopt:: .Vsp: MOV ECX,[EAX+DICT.Size] JECXZ .VsEM: ; If Dict End reached in dictionary table. CALL PseudopcDISPLAY.PutObjAsSysVar ADD EAX,SIZE#DICT JMP .Vsp: .VsEM:MOV EAX,DictEaoptMisc:: .VseM:MOV ECX,[EAX+DICT.Size] JECXZ .VsES: ; If Dict End reached in dictionary table. CALL PseudopcDISPLAY.PutObjAsSysVar ADD EAX,SIZE#DICT JMP .VseM: .VsES:MOV EAX,DictEaoptStatus:: .VseS:MOV ECX,[EAX+DICT.Size] JECXZ .VsEF: ; If Dict End reached in dictionary table. CALL PseudopcDISPLAY.PutObjAsSysVar ADD EAX,SIZE#DICT JMP .VseS: .VsEF:MOV EAX,DictEaoptFea:: .VseF:MOV ECX,[EAX+DICT.Size] JECXZ .VsS: ; If Dict End reached in dictionary table. CALL PseudopcDISPLAY.PutObjAsSysVar ADD EAX,SIZE#DICT JMP .VseF: .VsS: MOV EAX,DictEasmSysVar:: .Vss: MOV ECX,[EAX+DICT.Size] JECXZ .VsD: ; If Dict End reached in dictionary table. CALL PseudopcDISPLAY.PutObjAsSysVar ADD EAX,SIZE#DICT JMP .Vss: .VsD: XOR EDX,EDX ; Signalize that system %^variables are displayed. CALL PseudopcDISPLAY.DisplayVar RET ENDP PseudopcDISPLAY.SYSTEMVARIABLES:: PseudopcDISPLAY.PutObj PROC ; Common procedure for filterring and putting pointer to an object ; from EAX to [%SortBuf]. Object is SYM/SSS/VAR/MAC/DICT or anything starting with NamePtr/NameSize Qword. ; This PROC is used to gather object to [%SortBuf] before their sort and display. ; Input: EAX=pointer to object. ; EDX=pointer to QWORD (Ptr/Size) to filter. ; The filter is normalized (no leading %^ or trailing *), it may be empty. ; The filter is converted to uppercase in case of system %^variables. ; Output:ECX,ESI,EDI destroyed. ; Pointer to the object which passed the filter is added to [%SortBuf]. MOV ESI,[EAX+0] ; Object name ptr. MOV ECX,[EAX+4] ; Object name size. ;;; ;LEA EDX,[%MaskQV] JECXZ .Skip: ; If the object has no name, do not display. MOV EDI,[EDX+4] ; Normalized filtering mask size. TEST EDI JZ .Put: ; If empty filter specified, display the object. CMPB [ESI],'%%' ; Normalize object name (remove leading % or %^) just for filterring purpose. JNE .po5: INC ESI ; If object name begins with %, skip the percent sign. DEC ECX JZ .Skip: CMPB [ESI],'^' ; If object name begins with %^, skip those characters. JNE .po5: INC ESI DEC ECX .po5:CMP ECX,EDI JB .Skip: ; If the object name is shorter than filter, do not display. MOV ECX,EDI ; Compare only the number of characters in filter value. MOV EDI,[EDX+0] ; Filtering mask pointer. REPE CMPSB JNE .Skip: ; If name of object does not match the filter. .Put:PUSH EAX MOV EAX,ESP BufferStore [%SortBuf],EAX,4 ; Store pointer to the object with unnormalized name. POP EAX .Skip:RET ENDP PseudopcDISPLAY.PutObj PseudopcDISPLAY.PutObjAsSysVar PROC ; Similar to .PutObj but object type is VAR and its name is prefixed with %^. ; Input: EAX=pointer to object. ; EDX=pointer to QWORD (Ptr/Size) to uppercased filter. ; The filter is normalized (no leading %^ or trailing *), it may be empty. ; Output:ECX,ESI,EDI destroyed. ; Pointer to the object which passed the filter is added to [%SortBuf]. PUSH EAX MOV ESI,EAX ; ^object DICT. BufferNew [%StrBuf],8; Make room for Ptr/Size. MOV EDI,EAX ; ^Qword. BufferNew [%NameBuf],2 ; Make room for %^ prefix. MOV [EDI+0],EAX ; Ptr to future %^. MOVW [EAX],'%%^' MOV ECX,[ESI+4] ; Object name size. MOV ESI,[ESI+0] ; Pointer to object name without %. BufferStore [%NameBuf],ESI,ECX ; Store object name right behind %^. INC ECX INC ECX MOV [EDI+4],ECX ; Size of system %^variable name. MOV EAX,EDI CALL PseudopcDISPLAY.PutObj POP EAX RET ENDP PseudopcDISPLAY.PutObjAsSysVar PseudopcDISPLAY.Operand PROC ; Processing one operand (ordinal or keyword) of %DISPLAY statement. ; Input: EDI=^QW(Ptr,Size) with %DISPLAY key as specified by user. ; EDX=0 or ^QW(Ptr,Size) with raw filter value as specified by user. ; Output: any GPR but EBP may be destroyed. LEA ESI,[%MaskQV] ; Prepare mask for the case of empty filter (EDX=0). XOR EAX,EAX MOV [ESI+0],EAX MOV [ESI+4],EAX TEST EDX JZ .80: ; If no filter. ; Raw filter value EDX will be normalized and put to [%MaskQV]: ; leading % or %^ removed, trailing * removed. MOV ESI,[EDX+0] MOV ECX,[EDX+4] StripSpaces ESI,ECX JECXZ .80: ; If empty filter. CMPB [ESI+ECX-1],'*' JNE .20: DEC ECX ; Remove the trailing * from filter value. .20:JECXZ .80: ; If empty filter (nothing but *). CMPB [ESI],'%%' JNE .70: INC ESI ; Remove the leading % from filter value. DEC ECX JECXZ .80: ; If empty filter (nothing but %*). CMPB [ESI],'^' JNE .70: INC ESI ; Remove the leading %^ from filter value. DEC ECX .70:StripSpaces ESI,ECX LEA EDX,[%MaskQV] MOV [EDX+0],ESI MOV [EDX+4],ECX ; [%MaskQV] now has nonempty normalized filter value. .80:Invoke DictLookupObj::, [EDI+0],[EDI+4] ; Find the %DISPLAY category by (incomplete) key name. Msg cc=C,'3751',EDI,DictDisplayObj:: ; Invalid %%DISPLAY option "!1S". Expected !2L JC .90: MOV EBX,[%PgmPtr] CALL EAX ; Perform the %DISPLAY handler. .90: RET ENDP PseudopcDISPLAY.Operand
PseudopcFOR Procedure Stm ForPassPool LocalVar MOV EBX,[%Stm] MOV EAX,[EBX+STM.Program] TEST EAX JZ .90: MOV ECX,[EAX+PGM.PassPtr] TEST ECX JZ .90: MOV EAX,[ECX+PASS.Pool] TEST EAX JZ .90: MOV [%ForPassPool],EAX JNSt [EBX+STM.CtxStatusAll],ctxNoEmit,.20: ; %FOR in NoEmit will only push ctxFOR+ctxNoEmit. StackPush [Src.CtxStack::],0 MOV EDI,EAX Invoke Ctx1stRepeat:: ;Returns EAX=ctx1stRepeat only if all repeating contexts have it too. MOV ECX,ctxFOR+ctxRepeat+ctxNoEmit OR ECX,EAX Invoke CtxCreate::,EDI,ECX,EBX JMP .90: .20:; %FOR statement. We might get here either in normal stm flow or from corresponding %ENDFOR statement. StackPeekLast [Src.CtxStack::] JC .90: MOV EDI,EAX JNSt [EAX+CTX.Status],ctxFOR,.30: MOV EAX,[EDI+CTX.LinePtr] CMP EAX,[EBX+STM.LinePtr] JE .80: ; We came here from %ENDFOR, prepare the next formal %variable. .30:; New %FOR block encounterred in emitting status. Push ctxFOR context. Invoke StmCheckFields::,EBX,"10*?" JC .90: StackPush [Src.CtxStack::],0 MOV EDI,EAX ; New created context. Invoke Ctx1stRepeat:: ;Returns EAX=ctx1stRepeat only if all repeating contexts have it too. MOV ECX,ctxFOR+ctxExpansion+ctxExpandable+ctxFormal+ctxRepeat+ctxStep0 OR ECX,EAX Invoke CtxCreate::,EDI,ECX,EBX BufferNew [EDI+CTX.ObjBuffer],SIZE#CTX_FOR MOV [EDI+CTX.ObjPtr],EAX MOV ECX,[EBX+STM.NrOfOrdinals] MOV EBX,EAX ; EBX now points to the new uninitialized CTX_FOR object. SUB EAX,EAX MOV [EBX+CTX_FOR.NrOfOrdinals],ECX MOV [EBX+CTX_FOR.OrdinalNr],EAX ; Start with uninitialized ordinal number. MOV [EBX+CTX_FOR.Step+0],EAX ; Assume default step. MOV [EBX+CTX_FOR.Step+4],EAX ; Resolve keyword STEP= if present, otherwise leave default STEP=0. MOV EAX,[%Stm] BufferRetrieve [EAX+STM.KeyBuffer] JECXZ .60: .40:Invoke DictLookup::,DictForKeys::,[ESI+0],[ESI+4] ; Only STEP= expected. Msg cc=C,'3860',ESI ; Unexpected %FOR option "!1S=". Ignored. JC .50: Invoke ExpEvalNum::,[ESI+8],[ESI+12] JC.50: MOV [EBX+CTX_FOR.Step+0],EAX MOV [EBX+CTX_FOR.Step+4],EDX OR EAX,EDX JZ .50: RstSt [EDI+CTX.Status],ctxStep0 .50:ADD ESI,16 ; Inspect other keyword operands, if any, to detect unexpected keys. SUB ECX,16 ; JA .40: JG .40: .60:; EBX=^CTX_FOR is initialized. EDI=^CTX. ; Prepare formal variable name to CTX.FrmBuffer, persistently on pass pool. MOV EBX,[%Stm] MOV ECX,[EBX+STM.LabelSize] PoolStore [%ForPassPool],[EBX+STM.LabelPtr],ECX MOV EDX,EAX ; Ptr to persistent formal name. BufferNew [EDI+CTX.FrmBuffer],16 SUB ESI,ESI MOV [EAX+0],EDX ; Persistent formal name pointer. MOV [EAX+4],ECX ; Formal name size. MOV [EAX+8],ESI ; Formal value is not set yet. MOV [EAX+12],ESI ; Store statement ordinals to context.OrdBuffer with values persistent in .ValBuffer. BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .80: .70:MOV EAX,[ESI+0] ; %FOR statement ordinal value ptr. MOV EDX,[ESI+4] ; %FOR statement ordinal value size. PoolStore [%ForPassPool],EAX,EDX MOV EBX,EAX ; Persistent ordinal value ptr. BufferNew [EDI+CTX.OrdBuffer],8 MOV [EAX+0],EBX MOV [EAX+4],EDX ADD ESI,8 SUB ECX,8 JA .70: ; Copy all statement ordinals. .80:Invoke CtxForNext::,EDI ; Prepare formal variable for the 1st ot next expansion. JC .85: ; Exit if no more expansions required by %FOR ordinals. BufferRetrieve [EDI+CTX.FrmBuffer] ; 4*DD with formal name and value. JECXZ .85: Invoke LstSet::,[ESI+8],[ESI+12] Invoke CtxExpansionNrUpdate::,EDI,[%Stm] JNC .90: .85:SetSt [EDI+CTX.Status],ctxNoEmit+ctxExited ; If no more expansions or %MAXEXPANSIONS reached. SetSt [Src.Lst.Status::],lstNoData .90:EndProcedure PseudopcFOR
PseudopcEXITFOR Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.30: Invoke StmCheckFields::,EBX,"00?0" .30: Invoke CtxFind::, ctxFOR,EBX JNC .50: JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.90: Msg '7120',Dict_PseudopcEXITFOR:: ; Wrong nesting, unexpected !1S. JMP .90: .50: JZ .80: ; If blockId match. JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.80: Msg '7110',Dict_PseudopcEXITFOR::,EAX ; Wrong nesting, expected "!1S !2S". .80: SetSt [EAX+CTX.Status],ctxNoEmit+ctxExited ; Do not emit until %ENDFOR. .90:EndProcedure PseudopcEXITFOR
PseudopcENDFOR Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData Invoke CtxFind::,ctxFOR,EBX Msg cc=C,'7120',Dict_PseudopcENDFOR:: ; Wrong nesting, unexpected !1S. JC .90: JE .20: ; If blockId matched. JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.20: Msg '7110',Dict_PseudopcENDFOR::,EAX ; Wrong nesting, expected "!1S !2S". .20:MOV EDI,EAX ; ^CTX FOR. .30:; JSt [EDI+CTX.Status],ctxNoEmit, .70: JSt [EBX+STM.CtxStatusAll],ctxNoEmit, .70: ; FOR block not exited yet, leave context EDI on stack and goto %FOR statement. RstSt [EDI+CTX.Status],ctx1stRepeat MOV ECX,[EDI+CTX.LinePtr] MOV EDX,[EDI+CTX.ChunkPtr] MOV [EBX+STM.LineNext],ECX ; Continue expansion, go back to the %FOR statement. MOV [EBX+STM.ChunkNext],EDX JMPS .90: ; and leave FOR context on stack. .70:JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.80: Invoke StmCheckFields::,EBX,"00?0" .80:Invoke CtxDiscard::,EDI,EBX .90:EndProcedure PseudopcENDFOR
PseudopcIF Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData MOV ECX,[EBX+STM.CtxStatusAll] AND ECX,ctxNoEmit JNZ .60: OR ECX,ctxIfEmit ; %IF block in emitting status. RstSt [Src.Lst.Status::],lstNoData Invoke StmCheckFields::,EBX,"?0?0" JC .50: Invoke ExpEvalBoolOp1::,EBX Invoke LstBoolean:: JA .60: ; Jmp if TRUE .50:OR ECX,ctxNoEmit .60:OR ECX,ctxIF StackPush [Src.CtxStack::],0 Invoke CtxCreate::,EAX,ECX,EBX .90:EndProcedure PseudopcIF
PseudopcELSE Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData Invoke CtxFind::,ctxIF,EBX Msg cc=C,'7120',Dict_PseudopcELSE:: ; Wrong nesting, unexpected !1S. JC .90: JE .30: ; If blockId matched. JSt [EAX+CTX.Status],ctxIfEmit,.30: ; If %IF block was enterred in emitting state. SetSt [EAX+CTX.Status],ctxNoEmit ; %IF block enterred as NoEmit. Both branches are NoEmit. JMP .90: .30:Invoke StmCheckFields::,EBX,"00?0" JNSt [EAX+CTX.Status],ctxElsed,.50: Msg '7122' ; %ELSE was already used in this %IF/%ENDIF block. Ignored. JMP .90: .50:JNSt [EAX+CTX.Status],ctxIfEmit,.70: ; If %IF block was enterred in nonemitting state. TESTD [EAX+CTX.Status],ctxNoEmit Invoke LstBoolean:: ; List TRUE/FALSE in listing dump column. .70:InvSt [EAX+CTX.Status],ctxNoEmit ; Reverse the condition. SetSt [EAX+CTX.Status],ctxElsed RstSt [Src.Lst.Status::],lstNoData .90:EndProcedure PseudopcELSE
PseudopcENDIF Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData Invoke CtxFind::,ctxIF,EBX Msg cc=C,'7120',Dict_PseudopcENDIF:: ; Wrong nesting, unexpected !1S. JC .90: JE .20: ; If blockId matched. JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.20: Msg '7110',Dict_PseudopcENDIF::,EAX ; Wrong nesting, expected "!1S !2S". .20: Invoke StmCheckFields::,EBX,"00?0" Invoke CtxDiscard::,EAX,EBX .90:EndProcedure PseudopcENDIF
PseudopcMACRO Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData JNSt [EBX+STM.CtxStatusAll],ctxNoEmit,.30: ; In noemitting context only push ctxMACRO (%MACRO definition in %MACRO). StackPush [Src.CtxStack::],0 Invoke CtxCreate::,EAX,ctxMACRO+ctxDefinition+ctxNoEmit,EBX JMP .90: .30:; Emitting context, either macro declaration in normal stm flow, or expansion. Invoke StmCheckFields::,EBX,"10**" JC .80: JNSt [EBX+STM.CtxStatusAll],ctxPrototype,.50: ; We were sent here from macro invocation to update macro prototype and expand it. Invoke CtxPeek::,ctxMACRO,0 ; Macro expanding context already on stack does not change. JC .F9971: MOV EDI,EAX ; Context MACRO+Prototype. JNSt [EDI+CTX.Status],ctxPrototype,.F9971: Invoke MacPrototype::,EDI,EBX RstSt [EDI+CTX.Status],ctxPrototype Invoke CtxExpansionNrUpdate::,EDI,EBX JSt [EDI+CTX.Status],ctxMacLabeled,.90: ; There is no %: label in macro-definition body. Let us put macro label here, if any exists. BufferRetrieve [EDI+CTX.ObjBuffer] MOV EDX,[ESI+CTX_MAC.InvokStmStatus] MOV ECX,[ESI+CTX_MAC.LabelSize] MOV ESI,[ESI+CTX_MAC.LabelPtr] ; Invocation label is now ESI,ECX. As the macro-definition is not explicitly labeled, ; the label will be defined right here, at the start of expansion. JECXZ .40: ; If no label used on macro invocation. CMP ECX,1 JNE .34: CMPB [ESI],'$' JE .90: ; Macro was invoked with label $, ignore it. .34:AND EDX,stmLabelIsPublic SetSt [EBX+STM.Status],EDX MOVB [EBX+STM.Status],'A' ; Implicit macro label is type Address. SetSt EDX,symDefined Invoke SymCreate::,EDX,ESI,ECX,EBX .40:JMP .90: .F9971:Msg '9971' ; Internal error: unexpected error on macro expansion. JMP .90: .50:; We were sent here in normal flow of reading source: macro is being declared. MOV ESI,[EBX+STM.LabelPtr] MOV ECX,[EBX+STM.LabelSize] LEA EDX,[ESI+ECX] Invoke ExpParseIdentifier::,ESI,EDX,0 CMP EAX,ECX Msg cc=NE,'7319' ; Invalid character in the macro name. JNE .90: Invoke MacFind::, ESI,ECX Msg cc=NC,'2512',EBX,[EAX+MAC.LinePtr] ; Overwriting macro !1S previously defined at !2@. MOV ECX,[EBX+STM.Program] TEST ECX JZ .F9971: MOV ESI,[ECX+PGM.PassPtr] TEST ESI ; ESI=^PASS JZ .F9971: MOV ECX,EAX ; Either 0 or ^MAC if the macro was already defined. JECXZ .60: ListStore [ESI+PASS.MacList],ECX ; Copy previously defined macro. MOV EDI,EAX ; ^MAC on MacList. JMPS .70: .60:ListNew [ESI+PASS.MacList] MOV EDI,EAX ; ^MAC New empty leaf of MacList. MOV EAX,[EBX+STM.LabelPtr] MOV ECX,[EBX+STM.LabelSize] PoolStore [ESI+PASS.Pool],EAX,ECX JC .F9971: MOV [EDI+MAC.NamePtr],EAX MOV [EDI+MAC.NameSize],ECX .70:MOV EAX,[EBX+STM.LinePtr] MOV [EDI+MAC.LinePtr],EAX MOVD [EDI+MAC.Status],0 .80:StackPush [Src.CtxStack::],0 Invoke CtxCreate::,EAX,ctxMACRO+ctxDefinition+ctxFormal+ctxNoEmit,EBX MOV [EAX+CTX.MacPtr],EDI .90:EndProcedure PseudopcMACRO
PseudopcEXITMACRO Procedure Stm SetSt [Src.Lst.Status::],lstNoData MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: Invoke StmCheckFields::,EBX,"00?0" Invoke CtxFind::, ctxMACRO,EBX Msg cc=C,'7120',Dict_PseudopcEXITMACRO:: ; Wrong nesting, unexpected !1S. JC .90: Msg cc=NE,'7110',Dict_PseudopcEXITMACRO::,EAX ; Wrong nesting, expected "!1S !2S". SetSt [EAX+CTX.Status],ctxNoEmit+ctxExited ; Do not emit until %ENDMACRO .90:EndProcedure PseudopcEXITMACRO
PseudopcENDMACRO Procedure Stm SetSt [Src.Lst.Status::],lstNoData MOV EBX,[%Stm] Invoke CtxFind::,ctxMACRO,EBX Msg cc=C,'7120',Dict_PseudopcENDMACRO:: ; Wrong nesting, unexpected !1S. JC .90: Msg cc=NE,'7110',Dict_PseudopcENDMACRO::,EAX ; Wrong nesting, expected "!1S !2S". .20: MOV EDI,EAX ; ^CTX MACRO. JNSt [EDI+CTX.Status],ctxMacLabeled,.40: MOV EAX,[EDI+CTX.MacPtr] TEST EAX JZ .40: SetSt [EAX+MAC.Status],macLabeled .40: JSt [EDI+CTX.Status],ctxDefinition,.50: JNSt [EDI+CTX.Status],ctxExpansion,.50: ; Macro expansion ends. Assembly continues after macro invocation statement, saved in context. MOV EAX,[EDI+CTX.LineNext] MOV EDX,[EDI+CTX.ChunkNext] MOV [EBX+STM.LineNext],EAX MOV [EBX+STM.ChunkNext],EDX SetSt [Src.Lst.Status::],lstAsRepeated ; Separate listing column with '+' .50:Invoke StmCheckFields::,EBX,"00?0" ; Macro definition ends. .80:Invoke CtxDiscard::,EDI,EBX .90:EndProcedure PseudopcENDMACRO
PseudopcSHIFT Procedure Stm SetSt [Src.Lst.Status::],lstNoData ; Empty the dump column in listing. MOV EBX,[%Stm] StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: ; Do nothing in nonemitting state. Invoke StmCheckFields::,EBX,"00?0" ; Tolerate zero or one ordinal operand. JC .90: Invoke CtxPeek::, ctxMACRO,0 ; %SHIFT is acceptable only in macro context. Msg cc=C,'7120',Dict_PseudopcSHIFT:: ; Wrong nesting, unexpected !1S. JC .90: MOV EDI,EAX ; ^CTX BufferRetrieve [EBX+STM.OrdBuffer] MOV EAX,1 ; Default is %SHIFT +1. JECXZ .50: Invoke ExpEvalNum::,[ESI+0],[ESI+4] JC .90: .50: ADD [EDI+CTX.Shift],EAX ; Apply the shift value on the macro context. .90:EndProcedure PseudopcSHIFT
PseudopcDROPMACRO Procedure Stm SetSt [Src.Lst.Status::],lstNoData MOV EBX,[%Stm] StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: Invoke StmCheckFields::,EBX,"00+0" JC .90: SetSt [Src.Lst.Status::],lstNoData ; Check if %DROPMACRO has just one operand * (drop all macros). CMP [EBX+STM.NrOfOrdinals],1 JNE .30: BufferRetrieve [EBX+STM.OrdBuffer] MOV ECX,[ESI+4] CMP ECX,1 JNE .30: MOV ESI,[ESI+0] CMPB [ESI],'*' JNE .30: ; %DROPMACRO * - drop all macros declared so far in current PGM and parents. SUB EDI,EDI .10:Invoke CtxPeek::, ctxPROGRAM, EDI ; Find PGM on context stack. JC .90: ; End if no more programs. MOV EDI,EAX ; Context pointer. MOV EBX,[EAX+CTX.ObjPtr] ; Get the program to EBX. TEST EBX JZ .10: MOV ECX,[EBX+PGM.PassPtr] JECXZ .10: MOV EDX,[ECX+PASS.MacList] ListGetLast EDX .20:JZ .10: MOV [EAX+DICT.Data],0 ; Actual erasing of the macro EAX. ListGetPrev EAX JMP .20: .30:; Pseudoinstruction %DROPMACRO has explicitly enumerated macro names. ; Find current program.pass.MacList to EDX. ; Cannot use Invoke MacFind because only current program's macro can be %dropped. MOV ECX,[EBX+STM.Program] JECXZ .90: MOV ECX,[ECX+PGM.PassPtr] JECXZ .90: MOV EDX,[ECX+PASS.MacList] BufferRetrieve [EBX+STM.OrdBuffer] SAR ECX,3 JZ .90: .40:ListGetLast EDX .50:JZ .60: Compare [EAX+DICT.Ptr],[EAX+DICT.Size],[ESI+0],[ESI+4] JE .70: ListGetPrev EAX JMP .50: .60: ; Macro was not found in current program, let's search parents to learn if W2515 is due. Invoke MacFind::,[ESI+0],[ESI+4] Msg cc=C,'2515',PgmStatus=pgmLastPass,ESI ; Macro !1S was not found, cannot be dropped. ListStore EDX,ESI ; Create new DICT record on MacList with dropped macro name. ; The actual %DROPMACRO action is realized as zeroing current program's Maclist DICT.Data. .70:MOVD [EAX+DICT.Data],0 ; Actual erasing of the macro EAX. ADD ESI,8 LOOP .40: ; Repeat with each %DROPMACRO operand. .90:EndProcedure PseudopcDROPMACRO
PseudopcREPEAT Procedure Stm SetSt [Src.Lst.Status::],lstNoData MOV EBX,[%Stm] StackPeekLast [Src.CtxStack::] JC .90: JNSt [EAX+CTX.Status],ctxREPEAT,.30: MOV EDI,EAX ; ^CTX REPEAT. MOV EAX,[EDI+CTX.LinePtr] CMP EAX,[EBX+STM.LinePtr] JE .50: ; Do not push new ctxREPEAT if %REPEAT block continues expansion. .30:; New context will be created. Invoke StmCheckFields::,EBX,"00?0" Invoke Ctx1stRepeat:: ;Returns EAX=ctx1stRepeat only if all repeating contexts have it too. MOV ECX,ctxREPEAT+ctxExpandable+ctxExpansion+ctxRepeat OR ECX,EAX ; Incorporate ctx1stRepeat. StackPush [Src.CtxStack::],0 MOV EDI,EAX Invoke CtxCreate::,EDI,ECX,EBX JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.70: .50:Invoke CtxExpansionNrUpdate::,EDI,EBX JNC .90: .70:SetSt [EDI+CTX.Status],ctxNoEmit+ctxExited .90:EndProcedure PseudopcREPEAT
PseudopcEXITREPEAT Procedure Stm SetSt [Src.Lst.Status::],lstNoData MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.30: Invoke StmCheckFields::,EBX,"00?0" .30: Invoke CtxFind::, ctxREPEAT,EBX JNC .50: JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.90: Msg '7120',Dict_PseudopcEXITREPEAT:: ; Wrong nesting, unexpected !1S. JC .90: .50: JZ .80: JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.80: Msg '7110',Dict_PseudopcEXITREPEAT::,EAX ; Wrong nesting, expected "!1S !2S". .80: SetSt [EAX+CTX.Status],ctxNoEmit+ctxExited ; Do not emit until %ENDREPEAT. .90:EndProcedure PseudopcEXITREPEAT
PseudopcENDREPEAT Procedure Stm MOV EBX,[%Stm] Invoke CtxFind::,ctxREPEAT,EBX Msg cc=C,'7120',Dict_PseudopcENDREPEAT:: ; Wrong nesting, unexpected !1S. JC .90: JE .20: JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.20: Msg '7110',EAX,Dict_PseudopcENDREPEAT::,EAX ; Wrong nesting, expected "!1S !2S". .20: MOV EDI,EAX ; ^CTX REPEAT. JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.70: Invoke ExpEvalBoolOp1::, EBX Invoke LstBoolean:: JC .70: ; Block ends if error. JNZ .70: ; Block ends if TRUE. ; %UNTIL condition is FALSE, leave context EDI on stack and go back to %REPEAT. RstSt [EDI+CTX.Status],ctx1stRepeat MOV EAX,[EDI+CTX.LinePtr] MOV EDX,[EDI+CTX.ChunkPtr] MOV [EBX+STM.LineNext],EAX ; Continue expansion, go back to %REPEAT. MOV [EBX+STM.ChunkNext],EDX JMPS .90: .70:Invoke StmCheckFields::,EBX,"?0?0" Invoke CtxDiscard::,EDI,EBX .90:EndProcedure PseudopcENDREPEAT
PseudopcUNTIL Procedure Stm Invoke PseudopcENDREPEAT, [%Stm] EndProcedure PseudopcUNTIL
PseudopcSET Procedure Stm MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::, EBX, '10**' JC .99: Invoke VarCheckId::,[EBX+STM.LabelPtr],[EBX+STM.LabelSize] JC .99: Invoke EaBufferReserve::, PseudopcSET MOV EDX,EAX ; %SET will concatenate ordinal and keyword operands, separated with comma. BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .30: .10: ; ESI points to Ptr,Size of ordinal. BufferStore EDX,[ESI],[ESI+4] BufferStore EDX, =B',',1 ADD ESI,8 SUB ECX,8 JA .10: .30: BufferRetrieve [EBX+STM.KeyBuffer] JECXZ .50: .40: BufferStore EDX,[ESI],[ESI+4] BufferStore EDX,=B'=',1 BufferStore EDX,[ESI+8],[ESI+12] BufferStore EDX,=B',',1 ADD ESI,16 SUB ECX,16 JG .40: .50: BufferDecrement EDX ; Omit the last comma, if at least one was stored. BufferRetrieve EDX MOV EAX,[EBX+STM.Program] JNSt [EAX+PGM.Status],pgmLastPass,.97: Invoke LstSet::,ESI,ECX .97: Invoke VarAssign::,EBX,EDX ; Value of %variable is in buffer EDX. Invoke EaBufferRelease::,EDX .99:EndProcedure PseudopcSET
PseudopcSET2 Procedure Stm MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::, EBX, '10**' JC .99: Invoke EaBufferReserve::,PseudopcSET2 MOV EDX,EAX ; %SET2 will concatenate ordinal and keyword operands, separated with comma. BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .30: .10: ; ESI points to Ptr,Size of ordinal. BufferStore EDX,[ESI],[ESI+4] BufferStore EDX, =B',',1 ADD ESI,8 SUB ECX,8 JA .10: .30: BufferRetrieve [EBX+STM.KeyBuffer] JECXZ .50: .40: BufferStore EDX,[ESI],[ESI+4] BufferStore EDX,=B'=',1 BufferStore EDX,[ESI+8],[ESI+12] BufferStore EDX,=B',',1 ADD ESI,16 SUB ECX,16 JG .40: .50: BufferDecrement EDX ; Omit the last comma, if at least one was stored. BufferRetrieve EDX ADD ECX,ESI Invoke EaBufferReserve::,PseudopcSET2.50 Invoke VarExpandField::,ESI,ECX,EAX,-1 ; Second expansion. Invoke EaBufferRelease::,EDX Invoke VarAssign::,EBX,EAX ; Value of %variable is in buffer EAX. MOV ECX,[EBX+STM.Program] JNSt [ECX+PGM.Status],pgmLastPass,.98: BufferRetrieve EAX Invoke LstSet::,ESI,ECX .98: Invoke EaBufferRelease::,EAX .99:EndProcedure PseudopcSET2
PseudopcSETA Procedure Stm StmSaExp LocalVar Size=SIZE#EXP ; Evaluated EXP. StmSaBuf LocalVar ; Temporary ^buffer. StmSaDec LocalVar Size=20 ; Decadic value storage. MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::, EBX, '10*0' JC .99: Invoke VarCheckId::,[EBX+STM.LabelPtr],[EBX+STM.LabelSize] JC .99: Invoke EaBufferReserve::, PseudopcSETA MOV [%StmSaBuf],EAX ; Temorary buffer for %variable value. MOV EDX,EAX ; %SETA will calculate each ordinal operand and store decadic value as a list to EDX. BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .30: .10: ; ESI points to Ptr,Size of ordinal. LEA EDX,[%StmSaExp] Invoke ExpEval::,EDX,[ESI],[ESI+4],EBX Invoke ExpReportError::,EDX JC .20: Invoke ExpConvertToNumber::,EDX Msg cc=C,'7332',PgmStatus=pgmLastPass ; Plain numeric value or expression expected. JC .20: PUSH ECX LEA EDI,[%StmSaDec] MOV EAX,[EDX+EXP.Low] MOV EDX,[EDX+EXP.High] MOV ECX,EDI StoQD EDI SUB EDI,ECX BufferStore [%StmSaBuf],ECX,EDI POP ECX .20: ADD ESI,8 SUB ECX,8 JNA .30: BufferStore [%StmSaBuf],=B',',1 JMP .10: .30: Invoke VarAssign::,EBX,[%StmSaBuf] MOV EAX,[EBX+STM.Program] JNSt [EAX+PGM.Status],pgmLastPass,.98: BufferRetrieve [%StmSaBuf] Invoke LstSet::,ESI,ECX .98: Invoke EaBufferRelease::,[%StmSaBuf] .99:EndProcedure PseudopcSETA
PseudopcSETB Procedure Stm MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::, EBX, '10*0' JC .99: Invoke VarCheckId::,[EBX+STM.LabelPtr],[EBX+STM.LabelSize] JC .99: Invoke EaBufferReserve::, PseudopcSETB MOV EDX,EAX ; Temorary buffer for %variable value. BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .20: ; If no operand, treat this as FALSE. .10: ; ESI points to Ptr,Size of ordinal. Invoke ExpEvalBoolean::,[ESI+0],[ESI+4] JBE .20: BufferStore EDX,=B'1',1 JMPS .30: .20: BufferStore EDX,=B'0',1 .30: ADD ESI,8 SUB ECX,8 JG .10: .50: Invoke VarAssign::,EBX,EDX MOV EAX,[EBX+STM.Program] JNSt [EAX+PGM.Status],pgmLastPass,.80: BufferRetrieve EDX Invoke LstSet::,ESI,ECX .80:Invoke EaBufferRelease::,EDX .99:EndProcedure PseudopcSETB
PseudopcSETC Procedure Stm StmScExp LocalVar Size=SIZE#EXP ; Evaluated EXP. StmScBuf LocalVar ; Temporary ^buffer. StmScChar LocalVar ; ANSI-character value storage. MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::, EBX, '10*0' JC .99: Invoke VarCheckId::,[EBX+STM.LabelPtr],[EBX+STM.LabelSize] JC .99: Invoke EaBufferReserve::, PseudopcSETC MOV [%StmScBuf],EAX ; Temorary buffer for %variable value. MOV EDX,EAX ; %SETC will calculate each ordinal operand and store char as a string to buffer. BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .30: .10: ; ESI points to Ptr,Size of ordinal. LEA EDX,[%StmScExp] Invoke ExpEval::,EDX,[ESI],[ESI+4],EBX Invoke ExpReportError::,EDX JC .20: Invoke ExpConvertToNumber::,EDX Msg cc=C,'7332',PgmStatus=pgmLastPass ; Plain numeric value or expression expected. JC .20: PUSH ECX LEA EDI,[%StmScChar] MOV EAX,[EDX+EXP.Low] MOV [EDI],AL MOV ECX,EAX AND EAX,0xFFFFFF00 OR EAX,[EDX+EXP.High] Msg cc=NZ,'7333',ECX ; Value !1D does not fit as 8bit character.',0 BufferStore [%StmScBuf],EDI,1 POP ECX .20: ADD ESI,8 SUB ECX,8 JA .10: .30: Invoke VarAssign::,EBX,[%StmScBuf] MOV EAX,[EBX+STM.Program] JNSt [EAX+PGM.Status],pgmLastPass,.98: BufferRetrieve [%StmScBuf] Invoke LstSet::,ESI,ECX .98: Invoke EaBufferRelease::,[%StmScBuf] .99:EndProcedure PseudopcSETC
PseudopcSETE Procedure Stm StmSeBuf LocalVar ; Temporary ^buffer. MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::, EBX, '10*0' JC .99: Invoke VarCheckId::,[EBX+STM.LabelPtr],[EBX+STM.LabelSize] JC .99: Invoke EaBufferReserve::, PseudopcSETE MOV [%StmSeBuf],EAX ; Temorary buffer for %variable value. ; %SETE will calculate each ordinal operand and store text value as a list. BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .30: .10: ; ESI points to Ptr,Size of ordinal. MOV EDI,[ESI] MOV EDX,[ESI+4] MOV EAX,[%StmSeBuf] SysGetEnvironment EDI,EDX,EAX Msg cc=Z,'2520',ESI ; Environment variable "!1S" is undefined or empty. ADD ESI,8 SUB ECX,8 JNA .30: BufferStore [%StmSeBuf],=B',',1 JMP .10: .30: Invoke VarAssign::,EBX,[%StmSeBuf] MOV EAX,[EBX+STM.Program] JNSt [EAX+PGM.Status],pgmLastPass,.98: BufferRetrieve [%StmSeBuf] Invoke LstSet::,ESI,ECX .98: Invoke EaBufferRelease::,[%StmSeBuf] .99:EndProcedure PseudopcSETE
PseudopcSETL Procedure Stm StmSlBuf LocalVar ; Temporary ^buffer. StmSlDec LocalVar Size=20 ; Decadic value storage. MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::, EBX, '1010' JC .99: Invoke VarCheckId::,[EBX+STM.LabelPtr],[EBX+STM.LabelSize] JC .99: Invoke EaBufferReserve::, PseudopcSETL MOV [%StmSlBuf],EAX BufferRetrieve [EBX+STM.OrdBuffer] MOV ECX,[ESI+4] MOV ESI,[ESI] ; ESI,ECX should be unexpanded %variable name. LEA EDX,[ESI+ECX] PUSH ESI Invoke VarParseName::,ESI,EDX POP ESI Msg cc=C,'7841'; %%SETS and %%SETL expect %%variable name as the only operand. JC .95: Invoke VarExpand::,ESI,EDX,[%StmSlBuf],-1 ; EAX= is pointer behind the parsed %variable name and its suboperations ; za EAX by nemelo byt nic BufferRetrieve [%StmSlBuf] BufferClear [%StmSlBuf] Invoke ExpCountItems::,ESI,ECX LEA EDI,[%StmSlDec] SUB EDX,EDX MOV ECX,EDI StoQD EDI SUB EDI,ECX BufferStore [%StmSlBuf],ECX,EDI MOV EAX,[EBX+STM.Program] JNSt [EAX+PGM.Status],pgmLastPass,.94: Invoke LstSet::,ECX,EDI .94: Invoke VarAssign::,EBX,[%StmSlBuf] .95: Invoke EaBufferRelease::,[%StmSlBuf] .99: EndProcedure PseudopcSETL
PseudopcSETS Procedure Stm StmSsBuf LocalVar ; Temporary ^buffer. StmSsDec LocalVar Size=20 ; Decadic value storage. MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::, EBX, '1010' JC .99: Invoke VarCheckId::,[EBX+STM.LabelPtr],[EBX+STM.LabelSize] JC .99: Invoke EaBufferReserve::, PseudopcSETS MOV [%StmSsBuf],EAX BufferRetrieve [EBX+STM.OrdBuffer] MOV ECX,[ESI+4] MOV ESI,[ESI] ; ESI,ECX should be unexpanded %variable name. LEA EDX,[ESI+ECX] PUSH ESI Invoke VarParseName::,ESI,EDX POP ESI Msg cc=C,'7841'; %%SETS and %%SETL expect %%variable name as the only operand. JC .95: Invoke VarExpand::,ESI,EDX,[%StmSsBuf],-1 ; EAX= is pointer behind the parsed %variable name and its suboperations. BufferRetrieve [%StmSsBuf] BufferClear [%StmSsBuf] LEA EDI,[%StmSsDec] MOV EAX,ECX ; ValueSize SUB EDX,EDX MOV ECX,EDI StoQD EDI SUB EDI,ECX BufferStore [%StmSsBuf],ECX,EDI MOV EAX,[EBX+STM.Program] JNSt [EAX+PGM.Status],pgmLastPass,.94: Invoke LstSet::,ECX,EDI .94: Invoke VarAssign::,EBX,[%StmSsBuf] .95: Invoke EaBufferRelease::,[%StmSsBuf] .99: EndProcedure PseudopcSETS
PseudopcSETX Procedure Stm StmSxBuf LocalVar ; Temporary ^buffer for label name. MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::, EBX, '10**' JC .99: Invoke EaBufferReserve::,PseudopcSETX MOV [%StmSxBuf],EAX MOV EDX,EAX ; %SETX will concatenate ordinal and keyword operands, separated with comma. BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .30: .10: ; ESI points to Ptr,Size of ordinal. BufferStore EDX,[ESI],[ESI+4] BufferStore EDX, =B',',1 ADD ESI,8 SUB ECX,8 JA .10: .30: BufferRetrieve [EBX+STM.KeyBuffer] JECXZ .50: .40: BufferStore EDX,[ESI],[ESI+4] BufferStore EDX,=B'=',1 BufferStore EDX,[ESI+8],[ESI+12] BufferStore EDX,=B',',1 ADD ESI,16 SUB ECX,16 JG .40: .50: BufferDecrement EDX ; Omit the last comma, if at least one was stored. Invoke VarAssign::,EBX,EDX ; Value of %variable is in buffer EDX. MOV EAX,[EBX+STM.Program] JNSt [EAX+PGM.Status],pgmLastPass,.90: BufferRetrieve EDX Invoke LstSet::,ESI,ECX .90: Invoke EaBufferRelease::,[%StmSxBuf] .99:EndProcedure PseudopcSETX
PseudopcWHILE Procedure Stm MOV EBX,[%Stm] StackPeekLast [Src.CtxStack::] JC .90: JNSt [EAX+CTX.Status],ctxWHILE,.30: MOV EDI,EAX ; ^CTX WHILE MOV EAX,[EDI+CTX.LinePtr] CMP EAX,[EBX+STM.LinePtr] JE .50: ; Do not push new ctxWHILE if %WHILE block continues expansion. .30:; New context will be created. Invoke StmCheckFields::,EBX,"?0?0" JC .90: Invoke Ctx1stRepeat:: ; Returns EAX=ctx1stRepeat only if all repeating contexts have it too. MOV ECX,ctxWHILE+ctxExpandable+ctxRepeat+ctxExpansion OR ECX,EAX ; Incorporate ctx1stRepeat. StackPush [Src.CtxStack::],0 MOV EDI,EAX Invoke CtxCreate::,EDI,ECX,EBX JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.70: .50:Invoke ExpEvalBoolOp1::,EBX Invoke LstBoolean:: JBE .70: ; Jump if FALSE - %WHILE block is ending. Invoke CtxExpansionNrUpdate::,EDI,EBX JNC .90: .70: SetSt [EDI+CTX.Status],ctxNoEmit+ctxExited .90:EndProcedure PseudopcWHILE
PseudopcEXITWHILE Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.30: Invoke StmCheckFields::,EBX,"00?0" .30: Invoke CtxFind::, ctxWHILE,EBX JNC .50: JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.90: Msg '7120',Dict_PseudopcEXITWHILE:: ; Wrong nesting, unexpected !1S. JMP .90: .50: JZ .80: JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.80: Msg '7110',Dict_PseudopcEXITWHILE::,EAX ; Wrong nesting, expected "!1S !2S". .80: SetSt [EAX+CTX.Status],ctxNoEmit+ctxExited ; Do not emit until %ENDWHILE. .90:EndProcedure PseudopcEXITWHILE
PseudopcENDWHILE Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData Invoke CtxFind::,ctxWHILE,EBX Msg cc=C,'7120',Dict_PseudopcENDWHILE:: ; Wrong nesting, unexpected !1S. JC .90: JE .20: ; If blockId matched. JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.20: Msg '7110',Dict_PseudopcENDWHILE::,EAX ; Wrong nesting, expected "!1S !2S". .20: MOV EDI,EAX ; ^CTX WHILE. JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.70: ; Expansion of %WHILE block is ending. ; %WHILE condition was TRUE, leave context EDI on stack and goto %WHILE statement. RstSt [EDI+CTX.Status],ctx1stRepeat ; Do not list further expansions. MOV ECX,[EDI+CTX.LinePtr] MOV EDX,[EDI+CTX.ChunkPtr] MOV [EBX+STM.LineNext],ECX ; Continue expansion, go back to %WHILE statement. MOV [EBX+STM.ChunkNext],EDX JMPS .90: .70:JNSt [EBX+STM.CtxStatusAll],ctx1stRepeat,.80: Invoke StmCheckFields::,EBX,"00?0" .80:Invoke CtxDiscard::,EDI,EBX .90:EndProcedure PseudopcENDWHILE
PseudoPROC Procedure Stm Ii LocalVar Size=SIZE#II ClearLocalVar MOV EBX,[%Stm] LEA EDI,[%Ii] IiAllowModifier DIST Invoke StmGetIiModifiers::,EBX,EDI MOV EAX,[EDI+II.MfgExplicit] SUB EDI,EDI ; Pointer to an object if context is created. JNSt EAX,iiMfgNESTING_OFF,.10: JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: ; In noemitting status without nesting-check do not push context. JMPS .15: ; Emit ctxPROC with supressed nesting check. .10:MOV ECX,ctxNoEmit+ctxPROC+ctxNamespace JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.80: ; In noemitting status with standard nesting-check only push ctxPROC. .15:; Emit. StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. Invoke StmCheckFields::,EBX,"100*" ; Select code section. MOV EDI,[EBX+STM.Section] TEST EDI JZ .20: JSt [EDI+SSS.Purpose],sssPurposeCODE,.30: ; If no need to change the current section. JNSt [Ea::+EA.Eaopt+EAOPT.Status],eaoptAUTOSEGMENT, .25: ; AUTOSEGMENT is enabled, switch to the code section. .20: Invoke SssGetSegm::, [EBX+STM.Program], sssPurposeCODE; Find the first code section of this program. JC .90: MOV [EBX+STM.Section],EAX MOV EDI,EAX .25: Invoke SssCheckPurpose::,EDI,sssPurposeCODE ; Warn if a wrong section was chosen. .30: ; Set symbol properties. MOV EAX,[EDI+SSS.OrgLow] MOV EDX,[EDI+SSS.OrgHigh] MOV [EBX+STM.OffsetLow],EAX MOV [EBX+STM.OffsetHigh],EDX .40: LEA ECX,[%Ii] MOV EAX,stmProc JNSt [ECX+II.MfgExplicit],iiMfgDIST_FAR,.45: OR EAX,stmFar .45: JNSt [ECX+II.MfgExplicit],iiMfgDIST_NEAR,.50: OR EAX,stmNear .50: JSt EAX,stmNear|stmFar,.60: MOV ECX,[EBX+STM.Program] ; If PROC DIST= is not explicitly specified, query PROGRAM MODEL=. JECXZ .60: JSt [ECX+PGM.Pgmopt+PGMOPT.Status],pgmoptMEDIUM|pgmoptLARGE|pgmoptHUGE,.55: OR EAX,stmNear JMP .60: .55: OR EAX,stmFar .60: SetSt [EBX+STM.Status],EAX LEA ECX,[%Ii] ; Set alignment. Invoke ExpAlign::,[EBX+STM.OffsetLow],[ECX+II.Align],0 MOV [EBX+STM.AlignBytes],ECX ADD [EBX+STM.OffsetLow],ECX ADCD [EBX+STM.OffsetHigh],0 MOVB [EBX+STM.Status],'A' ; Create the symbol with proc name. MOV EAX,stmLabelIsPublic AND EAX,[EBX+STM.Status] OR EAX,symDefined Invoke SymCreate::,EAX,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EBX MOV EDI,EAX ; PROC symbol. .70: MOV ECX,ctxPROC+ctxNamespace LEA EDX,[%Ii] JNSt [EDX+II.MfgExplicit],iiMfgNESTING_OFF,.80: OR ECX,ctxNestingOff .80: StackPush [Src.CtxStack::],0 MOV EDX,EAX ; Create a new PROC context. Invoke CtxCreate::,EDX,ECX,EBX MOV [EAX+CTX.ObjPtr],EDI .90:EndProcedure PseudoPROC
PseudoENDPROC Procedure Stm Ii LocalVar Size=SIZE#II ; Required by StmGetIiModifiers. ClearLocalVar MOV EBX,[%Stm] LEA EDI,[%Ii] Invoke StmGetIiModifiers::,EBX,EDI StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. Invoke StmCheckFields::,EBX,"?0?*" ; Allow label field and one ordinal. Invoke CtxFind::,ctxPROC,EBX ; Check context. MOV ECX,EAX ; ^CTX or NULL if CF=1. LAHF ; Store flags to AH. JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.80: ; In noemitting status just pop the context, do not report errors. ; Emit. SAHF ; Restore the result of CtxFind to Eflags. Msg cc=C,'7120',Dict_PseudoENDPROC:: ; Wrong nesting, unexpected !1S. JC .90: JZ .30: ; Jump if block ids match. JSt [EDI+II.MfgExplicit],iiMfgNESTING_OFF,.30:; Ignore when ENDPROC NESTINGCHECK=DISABLED. Msg '7110',Dict_PseudoENDPROC::,ECX ; Wrong nesting, expected "!1S !2S". .30:MOV ESI,[ECX+CTX.BlockSect] ; Section might have been changed inside PROC..ENDP, restore it from context ECX. MOV EAX,[ESI+SSS.OrgLow] MOV EDX,[ESI+SSS.OrgHigh] MOV [EBX+STM.OffsetLow],EAX MOV [EBX+STM.OffsetHigh],EDX MOV [EBX+STM.Section],ESI MOV EAX,[EBX+STM.Program] MOV [EAX+PGM.CurrentSect],ESI JNSt [EBX+STM.Status],stmLabelPresent,.50: MOV EAX,symDefined ; The statement ENDPPROC may have a label and ordinal operand. JNSt [EBX+STM.Status],stmLabelIsPublic,.40: OR EAX,symGlobalRef .40:Invoke SymCreate::,EAX,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EBX .50:; Update size of the symbol defined by the corresponding PROC. It will be the size of PROC..ENDP code. MOV EDI,[ECX+CTX.ObjPtr] ; EDI=^SYM defined by PROC. TEST EDI JZ .90: MOV EAX,[EBX+STM.OffsetLow] MOV EDX,[EBX+STM.OffsetHigh] SUB EAX,[EDI+SYM.OffsetLow] SBB EDX,[EDI+SYM.OffsetHigh] Msg cc=B,'7167',EDI ; Size of procedure "!1S" exceeds 2 GB. CMP EAX,[EDI+SYM.Size] MOV [EDI+SYM.Size],EAX JE .80: RstSt [EDI+SYM.Status],symFixed .80:JECXZ .90: Invoke CtxDiscard::,ECX,EBX .90:EndProcedure PseudoENDPROC
PseudoPROC1 Procedure Stm PreviousSect LocalVar ; ^SSS on PROC1 entry, must be restored after ENDPROC1. Ii LocalVar Size=SIZE#II ClearLocalVar MOV EBX,[%Stm] MOV EAX,[EBX+STM.Section] LEA EDI,[%Ii] MOV [%PreviousSect],EAX IiAllowModifier DIST Invoke StmGetIiModifiers::,EBX,EDI MOV EAX,[EDI+II.MfgExplicit] SUB EDI,EDI ; Pointer to the object if context is created. JNSt EAX,iiMfgNESTING_OFF,.10: JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: ; In noemitting status without nesting-check do not push context. JMPS .20: ; Emit ctxPROC1 with supressed nesting check. .10:MOV ECX,ctxNoEmit+ctxPROC1+ctxNamespace JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.70: ; In noemitting status with standard nesting-check only push ctxPROC1. .20:StmInStruc EBX,.90: ; Emit. Do not allow this statement in a structure definition. Invoke StmCheckFields::,EBX,"100*" Invoke EaBufferReserve::,PseudoPROC1 ; If PROC1 symbol was already created in this pass, the block will not emit. Invoke SymDelocalName::,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EAX,memberDelocal BufferRetrieve EAX Invoke EaBufferRelease::,EAX Invoke SymFindByName::,0,ESI,ECX,[EBX+STM.Program] JC .30: ; When PROC1 statement was encounterred for the first time. MOV ECX,ctxNoEmit+ctxPROC1+ctxNamespace JSt [EAX+SYM.Status],symDefInPass|symExtern,.70: ; If PROC1 was already declared or it is external, do not emit. .30:Invoke SssCreate@RT::,[%PreviousSect],EBX ; Create or update PROC1 symbol. Select CODE runtime section. MOV [EBX+STM.Section],EAX ; Switch to the runtime section. MOV EDX,[EAX+SSS.OrgHigh] ; Set symbol properties. MOV EAX,[EAX+SSS.OrgLow] MOV [EBX+STM.OffsetHigh],EDX MOV [EBX+STM.OffsetLow],EAX LEA ECX,[%Ii] MOV EAX,stmProc+stm1 JNSt [ECX+II.MfgExplicit],iiMfgDIST_FAR,.40: OR EAX,stmFar .40:JNSt [ECX+II.MfgExplicit],iiMfgDIST_NEAR,.45: OR EAX,stmNear .45:JSt EAX,stmNear|stmFar,.55: MOV ECX,[EBX+STM.Program] ; If PROC DIST= is not explicitly specified, query PROGRAM MODEL=. JECXZ .55: JSt [ECX+PGM.Pgmopt+PGMOPT.Status],pgmoptMEDIUM|pgmoptLARGE|pgmoptHUGE,.50: OR EAX,stmNear JMP .55: .50:OR EAX,stmFar .55:SetSt [EBX+STM.Status],EAX .60:SetSt [EBX+STM.Status],stmProc LEA ECX,[%Ii] ; Set alignment. Invoke ExpAlign::,[EBX+STM.OffsetLow],[ECX+II.Align],0 MOV [EBX+STM.AlignBytes],ECX ADD [EBX+STM.OffsetLow],ECX ADCD [EBX+STM.OffsetHigh],0 MOVB [EBX+STM.Status],'A' ; Create the symbol with proc1 name. MOV EAX,stmLabelIsPublic AND EAX,[EBX+STM.Status] OR EAX,symDefined Invoke SymCreate::,EAX,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EBX MOV EDI,EAX ; PROC1 symbol. MOV ECX,ctxPROC1+ctxNamespace .70:LEA EDX,[%Ii] JNSt [EDX+II.MfgExplicit],iiMfgNESTING_OFF,.80: OR ECX,ctxNestingOff .80:StackPush [Src.CtxStack::],0 MOV EDX,EAX ; Create new PROC1 context. Invoke CtxCreate::,EDX,ECX,EBX MOV ECX,[%PreviousSect] MOV [EDX+CTX.ObjPtr],EDI MOV [EDX+CTX.PreviousSect],ECX .90:EndProcedure PseudoPROC1
PseudoENDPROC1 Procedure Stm Ii LocalVar Size=SIZE#II ; Required by StmGetIiModifiers. ClearLocalVar MOV EBX,[%Stm] LEA EDI,[%Ii] Invoke StmGetIiModifiers::,EBX,EDI StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. Invoke StmCheckFields::,EBX,"?0?*" ; Allow label field and one ordinal. Invoke CtxFind::,ctxPROC1,EBX ; Check context. MOV ECX,EAX ; ^CTX or NULL if CF=1. LAHF ; Store flags to AH. JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.70: ; In noemitting status just pop the context, do not report errors. ; Emit. SAHF ; Restore the result of CtxFind to Eflags. Msg cc=C,'7120',Dict_PseudoENDPROC1:: ; Wrong nesting, unexpected !1S. JC .90: JZ .30: ; Jump if block ids match. JSt [EDI+II.MfgExplicit],iiMfgNESTING_OFF,.30:; Ignore when ENDPROC NESTINGCHECK=DISABLED. Msg '7110',Dict_PseudoENDPROC1::,ECX ; Wrong nesting, expected "!1S !2S". .30:MOV ESI,[ECX+CTX.BlockSect] ; If @RTx section was changed inside PROC1..ENDP1, MOV EAX,[ESI+SSS.OrgLow] ; restore it temporarily to match [@RT*] chosen for PROC1 statement. MOV EDX,[ESI+SSS.OrgHigh] MOV [EBX+STM.Section],ESI MOV [EBX+STM.OffsetLow],EAX MOV [EBX+STM.OffsetHigh],EDX JNSt [EBX+STM.Status],stmLabelPresent,.50: MOV EAX,symDefined ; The statement ENDPPROC1 may have a label. JNSt [EBX+STM.Status],stmLabelIsPublic,.40: OR EAX,symGlobalRef .40:Invoke SymCreate::,EAX,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EBX .50:; Update size of the symbol defined with the corresponding PROC1. It will be the size of PROC1..ENDP1 code. MOV EDI,[ECX+CTX.ObjPtr] ; EDI=^SYM defined by PROC1. MOV EAX,[EBX+STM.OffsetLow] MOV EDX,[EBX+STM.OffsetHigh] SUB EAX,[EDI+SYM.OffsetLow] SBB EDX,[EDI+SYM.OffsetHigh] Msg cc=B,'7167',EDI ; Size of procedure "!1S" exceeds 2 GB. CMP EAX,[EDI+SYM.Size] MOV [EDI+SYM.Size],EAX JE .70: RstSt [EDI+SYM.Status],symFixed .70:MOV ESI,[ECX+CTX.PreviousSect] ; Restore the section which preceeded PROC1 statement. Invoke PgmGetCurrent:: JC .80: MOV [EAX+PGM.CurrentSect],ESI SetSt [EBX+STM.Flags],stmtKeepSect ; Keep PGM.CurrentSect not updated from STM.Section. .80:JECXZ .90: Invoke CtxDiscard::,ECX,EBX .90:EndProcedure PseudoENDPROC1
PseudoPROGRAM Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData MOVD [Src.Lst.Section::],0 JNSt [EBX+STM.CtxStatusAll],ctxNoEmit,.20: .10: ; PROGRAM statement in nonemitting state will only push nonemitting ctxPROGRAM. PGM is not created. StackPush [Src.CtxStack::],0 Invoke CtxCreate::,EAX,ctxPROGRAM+ctxNamespace+ctxNoEmit,EBX JMP .90: ; Do nothing else in nonemitting state. .20: Invoke CtxFind::,ctxPROGRAM,0 ; Return parent program context in EAX. JC .40: ; Skip if the statement is envelope PROGRAM (no other program context is on CtxStack). MOV EDI,EAX ; Current program context. JSt [EDI+CTX.Status],ctxPgmReturned,.70: ; If arrived to PROGRAM from ENDPROGRAM (next pass begins). ; Otherwise arrived here in normal statements flow. Start to assemble the new program if parent is in the final pass. MOV ECX,[EDI+CTX.ObjPtr] ; Get Pgm object from context stack. JECXZ .10: JNSt [ECX+PGM.Status],pgmLastPass,.10: ; If parent is not in final pass, skip the entire PROGRAM/ENDPROGRAM block. ; Entered the statement PROGRAM in emitting state, parent program is in its final pass. .40: ; Arrived here in normal stream of statements. A new program is created. Invoke StmCheckFields::,EBX,"100*" JC .90: StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. StackPush [Src.CtxStack::],0 ; Allocate room for the new program context. MOV EDI,EAX ; EDI=New program context. Invoke CtxCreate::,EDI,ctxPROGRAM+ctxNamespace,EBX Invoke PgmCreateProgram::,EBX ; New program starts, its ptr is returned in EAX. MOV ECX,EAX ; ECX=^PGM. MOV [EDI+CTX.ObjPtr],EAX .70:RstSt [EDI+CTX.Status],ctxPgmReturned JNSt [ECX+PGM.Status],pgmEnvelope,.80: JNSt [ECX+PGM.Status],pgmEnvelDirty,.75: SetSt [Src.Lst.Status::],lstEnvelope+lstListOn JMP .80: .75:SetSt [Src.Lst.Status::],lstEnvelope+lstNoList .80:Invoke PgmParameters::,ECX,EBX ; Assemble PROGRAM keywords again, report errors. LEA EAX,[ECX+PGM.Pgmopt] Invoke PgmoptSetDefaults::,EAX Invoke PgmoptCheck::,ECX CMPD [ECX+PGM.PassNr],1 JA .85: Invoke SssCreateImplicit::,ECX ; Only when PROGRAM was encountered in pass 1. .85: Invoke PassCreate::,ECX .90: EndProcedure PseudoPROGRAM
PseudoENDPROGRAM Procedure Stm MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstNoData Invoke CtxFind::,ctxPROGRAM,EBX Msg cc=C,'7120',Dict_PseudoENDPROGRAM:: ; Wrong nesting, unexpected !1S. JC .90: Msg cc=NE,'7110',Dict_PseudoENDPROGRAM::,EAX ; Wrong nesting, expected "!1S !2S". MOV ESI,EAX ; ^CTX PROGRAM. JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.80: ; In noemitting status the block terminates, context is popped. ; ENDPROGRAM in emitting state will terminate current pass and redirect assembly ; back to the corresponding PROGRAM statement, unless this pass was the last. Invoke StmCheckFields::,EBX,"00?0" StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. MOVD [EBX+STM.Section],0 ; Prevent listing to show the last [section] from previous pass. MOV EDI,[ESI+CTX.ObjPtr] ; EDI=^PGM. TEST EDI JZ .90: MOVD [EDI+PGM.CurrentSect],0 ; Prevent listing to show the last [section] from previous pass. JNSt [EDI+PGM.Status],pgmEnvelope,.40: Invoke PgmCheckDirty::,EDI JNC .30: ; If envelope is not dirty, turn the listing of ENDPROGRAM statement off. SetSt [EDI+PGM.Status],pgmEnvelDirty SetSt [Src.Lst.Status::],lstEnvelope+lstListOn JMP .40: .30: SetSt [Src.Lst.Status::],lstEnvelope+lstNoList .40: Invoke PassInspect::,EDI ; Return CF if at least one more pass is due. Set pgmLastPass if it will be the last one. JNC .50: ; Skip when no more passes are needed. JNSt [EDI+PGM.Status],pgmLastPass,.45: SetSt [EDI+PGM.Status],pgmLastJustSet ; Tell SrcAssemble to skip this statement (ENDPROGRAM) in listing. .45: Invoke PassDestroy::,EDI ; Another pass is required. It may be the last. BufferClear [EBX+STM.MsgBuffer] ; Erase messages gathered in nonlisting pass. MOVB [MsgPgmStatus::],0 ; Cancel possible msg suppressing from nonlisted error messages from previous pass. StackPeekLast [Src.CtxStack::] JNSt [EAX+CTX.Status],ctxPROGRAM,.70: ; Abort the program if context mismatch. MOV ECX,[ESI+CTX.LinePtr] ; Redirect assembly progress back to the statement PROGRAM. MOV EDX,[ESI+CTX.ChunkPtr] MOV [EBX+STM.LineNext],ECX ; Continue assembly with PROGRAM statement, MOV [EBX+STM.ChunkNext],EDX ; which will be skipped due to ctxPgmPassed. SetSt [ESI+CTX.Status],ctxPgmPassed+ctxPgmReturned ; Mark the program as passed, but do not destroy it. Leave it on stack. JMP .90: .50: ; The last pass has just ended. Before destroying its pool, %variables and macros ; defined in this program EDI will be merged to the parent program (they are visible in the entire source). Invoke CtxPeek::,ctxPROGRAM,ESI ; Find parent program above context of this program. JC .60: ; Skip merging if no parent exists, i.e. EDI is an envelope program. MOV ECX,[EAX+CTX.ObjPtr] JECXZ .60: Invoke VarListMerge::,[ECX+PGM.PassPtr],[EDI+PGM.PassPtr] Invoke MacListMerge::,[ECX+PGM.PassPtr],[EDI+PGM.PassPtr] .60: JNSt [EDI+PGM.Pgmopt.Status],pgmoptLISTLITERALS,.70: Invoke PgmListLiterals::,EDI .70: SUB EAX,EAX MOV [EBX+STM.Section],EAX Invoke PassDestroy::,EDI Invoke PgmLinkSections::,EDI Invoke PgmDestroy::, EDI,EBX ; Combine and link modules and store the output file. .80: Invoke CtxDiscard::,ESI,EBX ; Continue assembly with the statement which follows ENDPROGRAM. Invoke CtxPeek::,ctxPROGRAM,0 ; Find the parent program, if exists. JC .90: TEST EAX JZ .90: MOV ECX,[EAX+CTX.ObjPtr] JECXZ .90: MOV [EBX+STM.Program],ECX ; Program was assembled and written. Continue with its parent. .90:EndProcedure PseudoENDPROGRAM
PseudoSEGMENT Procedure Stm SegmPtr LocalVar MOV EBX,[%Stm] StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::,EBX,"100*" JC .90: ; Check segment name. Label field must be in braces []. MOV ECX,[EBX+STM.LabelSize] MOV ESI,[EBX+STM.LabelPtr] JECXZ .E6815: CMPB [ESI],'[' JE .05: .E7831:Msg '7831',EBX ; Segment name "!1S" must be in braces []. JMP .90: .E6815:Msg '6815' ; Segment name in [] is expected in the label field of SEGMENT definition. JMP .90: .05: CMPB [ESI+ECX-1],']' JNE .E7831: INC ESI SUB ECX,2 StripSpaces ESI,ECX JECXZ .E6815: MOV EDX,[EBX+STM.Program] ; AUTOSEGMENT is silently switched OFF by any statement which changes|declares section|segment. RstSt [Ea.Eaopt.Status::],eaoptAUTOSEGMENT Invoke SssFindByName::, sssTypeMask,0,ESI,ECX,0 ; Find any older segment, section or structure with this name. MOV EDI,EAX JC .20: ; Jump if the name is free (not found). ; SSS object EDI already exist in the list. This may be OK if redeclaring implicit virgin segment. JSt [EDI+SSS.Status],sssImplicit,.10: JNSt [EDI+SSS.Status],sssDefinedInPass, .30: ; OK when it was declared in previous pass and not in this one. .E7833:Msg '7833',EDI,[EDI+SSS.LinePtr] ; Segment, section or structure [!1S] was already declared in !2@. JMP .90: .E7835:Msg '7835',EDI,PgmStatus=pgmLastPass ; Implicit segment [!1S] cannot be redeclared, it is not empty. JMP .90: .10: Invoke SssCheckDirty::,EDI,EDX ; Implicit segment may be redeclared only if it was not used before. JC .E7835: ; Virgin implicit segment EDI is redeclared. ; EBX=^STM, EDX=^PGM, EDI=^SSS. MOV EAX,[EBX+STM.LinePtr] MOV [EDI+SSS.LinePtr],EAX RstSt [EDI+SSS.Status],sssImplicit JMP .30: .20: MOV EDI,[EDX+PGM.Pgmopt.Status] AND EDI,pgmoptWidthMask ; pgmoptWidth flags have assigned same values as sssWidth. OR EDI,sssSegment+sssSection+sssPublic+sssDefinedInPass ; Default new segment status. Invoke SssCreateSe::,EBX,0,ESI,ECX,EDI,0,16 MOV EDI,EAX .30: SetSt [EDI+SSS.Status],sssDefinedInPass MOV [EBX+STM.Section],EDI ; Make the just created section the current section. ; Keyword operands of SEGMENT statement may update its properties. BufferRetrieve [EBX+STM.KeyBuffer] LEA EDX,[ESI+ECX] .50: CMP ESI,EDX ; Process all keyword parameters. JNB .90: MOV ECX,ESI ; Prepare ECX=pointer to key name for the case of E7800. LODSD ; Key NamePtr. MOV EDI,EAX LODSD ; Key NameSize. Invoke DictLookup::, DictSegmentKeys::, EDI,EAX PUSH EAX LEA EAX,[EBX+STM.OperationPtr] Msg cc=C,'7800',EAX,ECX,DictSegmentKeys:: ; Unknown %1S keyword "!2S". Expected one of !3L. POP ECX ; PseudoSEGMENT.Keyword handler. LODSD ; Key ValuePtr. MOV EDI,EAX LODSD ; Key ValueSize. JC .50: StripSpaces EDI,EAX JMP ECX ; Key value handler. ; Segment key value handlers. Key value is in EDI,EAX. ESI is ptr behind 4*DWORD with key. ; ESI,EDX,EBX,EBP must be preserved in value handlers. .Purpose:: ; Multiple purposes may be separated with '+' or '|', e.g. "PURPOSE=Data| Bss" PUSH EDX,ESI MOV ESI,EDI ; Value start. LEA EDX,[EDI+EAX] ; Value end. MOV EDI,[EBX+STM.Section] MOVD [EDI+SSS.Purpose],0 ; Remove old purposes first. .P1: CMP ESI,EDX ; Simplified purpose value parsing begins. JNB .P9: LODSB ExpClassify AL CMP AH,expWhiteSpace JE .P1: CMP AL,'+' JE .P1: CMP AL,'|' JE .P1: CMP AH,expLetter Msg cc=NE,'6531',EAX ; Illegal character "!1Z" in purpose specification. Expected "PURPOSE=DATA|CODE|BSS" JNE .P9: LEA EDI,[ESI-1] ; Start of purpose name. .P2: CMP ESI,EDX JNB .P3: LODSB ExpClassify AL CMP AH,expLetter JE .P2: DEC ESI .P3: MOV EAX,ESI SUB EAX,EDI Invoke DictLookup::, DictSegmentPurpose::, EDI,EAX JC .E7805: MOV EDI,[EBX+STM.Section] SetSt [EDI+SSS.Purpose],EAX JMP .P1: .E7805: POP ESI,EDX LEA EDI,[ESI-16] LEA ECX,[ESI-8] Msg '7805',EDI,ECX,DictSegmentPurpose:: ; Unknown %1S value "!2S". Expected one of !3L. JMP .50: .P9: POP ESI,EDX JMP .50: .Width:: PUSH EDX Invoke ExpEvalNum::,EDI,EAX ; Evaluates to EDX:EAX. POP EDX MOV ECX,sssWidth64 CMP EAX,64 JE .Width5: MOV ECX,sssWidth32 CMP EAX,32 JE .Width5: MOV ECX,sssWidth16 CMP EAX,16 Msg cc=NE,'6525',EAX ; Invalid width !1D, must be 16 or 32 or 64. JNE .50: .Width5: MOV EDI,[EBX+STM.Section] RstSt [EDI+SSS.Status],sssWidthMask SetSt [EDI+SSS.Status],ECX Dispatch ECX,sssWidth32,sssWidth16 JSt [Ea.Eaopt.Machine::],iiCPU_X64,.50: PUSH ESI Invoke EaBufferReserve::,PseudoSEGMENT Invoke IiCpuToKeys::,iiCPU_X64,0,EAX BufferRetrieve EAX Invoke EaBufferRelease::,EAX PUSH ECX,ESI MOV EAX,ESP Msg '2340',EAX ; This instruction requires option EUROASM "!1S". POP ESI,ECX POP ESI JMP .50: .sssWidth32: JSt [Ea.Eaopt.Machine::],iiCPU_386,.50: PUSH ESI Invoke EaBufferReserve::,PseudoSEGMENT Invoke IiCpuToKeys::,iiCPU_386,0,EAX BufferRetrieve EAX Invoke EaBufferRelease::,EAX PUSH ECX,ESI MOV EAX,ESP Msg '2340',EAX ; This instruction requires option EUROASM "!1S". POP ESI,ECX POP ESI .sssWidth16: JMP .50: .Align:: Invoke ExpParseAlignment::,EDI,EAX,-1 JC .50: ; EAX is one of 0,1,2,4,8..512. MOV EDI,[EBX+STM.Section] MOV [EDI+SSS.Alignment],EAX JMP .50: .Combine:: Invoke DictLookup::, DictSegmentCombine::, EDI,EAX LEA EDI,[ESI-16] LEA ECX,[ESI-8] Msg cc=C,'7805',EDI,ECX,DictSegmentCombine:: ; Unknown %1S value "!2S". Expected one of !3L. JC .50: MOV EDI,[EBX+STM.Section] RstSt [EDI+SSS.Status],sssCombineMask SetSt [EDI+SSS.Status],EAX JMP .50: .Class:: MOV ECX,EAX StripSpaces EDI,ECX StripQuotes EDI,ECX StripApostrophes EDI,ECX PUSH ESI MOV ESI,[EBX+STM.Program] PoolStore [ESI+PGM.Pool],EDI,ECX JC .Cl8: MOV ESI,[EBX+STM.Section] MOV [ESI+SSS.ClassPtr],EAX MOV [ESI+SSS.ClassSize],ECX .Cl8:POP ESI JMP .50: .90: SetSt [Src.Lst.Status::],lstSectInline ; Display [Segm] in dump column on the same line. MOVD [Src.Lst.Section::],0 ; Force listing to display [Segment] in dump column. MOV EDI,[EBX+STM.Section] Invoke SssGuessPurpose::,EDI Msg cc=Z,'6530' ; Please specify SEGMENT PURPOSE= (combination of DATA|CODE|BSS). .99:EndProcedure PseudoSEGMENT
PseudoGROUP Procedure Stm MOV EBX,[%Stm] StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99: Invoke StmCheckFields::,EBX,"10*0" JC .90: ; Check group name. Label field must be in braces []. MOV ESI,[EBX+STM.LabelPtr] MOV ECX,[EBX+STM.LabelSize] JECXZ .E6814: CMPB [ESI],'[' JE .05: .E7831:Msg '7831',EBX ; Segment, group or section name "!1S" must be in braces []. JMP .90: .E6814:Msg '6814' ; Group name in [] is expected in the label field of GROUP definition. JMP .90: .05: CMPB [ESI+ECX-1],']' JNE .E7831: INC ESI SUB ECX,2 StripSpaces ESI,ECX JECXZ .E6814: MOV EDX,[EBX+STM.Program] Invoke SssFindByName::, sssGroup,0,ESI,ECX,0; Find any older group with this name. MOV EDI,EAX JC .10: ; If the name is unoccupied yet. ; Group EDI already exists in the list. JNSt [EDI+SSS.Status],sssDefinedInPass,.30: ; Skip W2931 if it was declared in previous pass. .W2931:Msg '2931',EDI,[EDI+SSS.LinePtr] ; Updating group [!1S] previously declared in !2@. JMP .30: .E7838:Msg '7838',EAX ; [!1S] is not 16bit segment, it cannot be member of a group. JMP .80: .10: ; Create a new group with name in ESI,ECX. Invoke SssCreateGroup::,EBX,ESI,ECX,sssGroup,0 Invoke SymCreateSe::,EAX,[EBX+STM.Program] MOV EDI,EAX .30: ; Group EDI is being defined/updated. Each ordinal specifies a segment, whose .GroupPtr will be set to EDI. BufferRetrieve [EBX+STM.OrdBuffer] LEA EDX,[ESI+ECX] .40: CMP ESI,EDX JNB .90: PUSH EDX,ESI MOV EDX,ESI ; Prepare EDX as !1S for the case of error. LODSD MOV ECX,EAX LODSD MOV ESI,ECX MOV ECX,EAX StripSpaces ESI,ECX ; Check segment name ESI,ECX. Name must be in braces []. CMPB [ESI],'[' JE .50: .E7831b:Msg '7831',EDX ; Segment name "!1S" must be in braces []. JMP .80: .50: CMPB [ESI+ECX-1],']' JNE .E7831b: INC ESI SUB ECX,2 StripSpaces ESI,ECX JECXZ .E7831b: ; Find a segment (group member) by name ESI,ECX. If not exists, create it. Invoke SssFindByName,sssTypeMask,0,ESI,ECX,0 JC .60: JSt [EAX+SSS.Status],sssSegment, .70: Msg '7830',EAX,[EAX+SSS.LinePtr] ; Object [!1S] defined in !2@ is not a segment. JMP .80: .60: ; Pseudoinstruction GROUP declares segment which was not defined yet. Let's create it (with default width and alignment). MOV EAX,[EBX+STM.Program] MOV EAX,[EAX+PGM.Pgmopt.Status] AND EAX,pgmoptWidthMask OR EAX,sssSegment+sssSection+sssPublic+sssDefinedInGroup Invoke SssCreateSe::,EBX,0,ESI,ECX,EAX,0,16 MOV ECX,[EBX+STM.LinePtr] MOV [EAX+SSS.SegmPtr],EAX MOV [EAX+SSS.GroupPtr],EDI MOV [EAX+SSS.LinePtr],ECX .70: MOV EDX,[EAX+SSS.GroupPtr] ; Warn when group membership changes. MOV [EAX+SSS.GroupPtr],EDI TEST EDX JZ .80: CMP EDX,EDI JE .80: Msg '3203',EAX,EDX,[EDX+SSS.LinePtr] ; Segment [!1S] was member of group [!2S] defined in !3@. .80: POP ESI,EDX ADD ESI,8 JMP .40: ; Try the next ordinal operand. .90: SetSt [Src.Lst.Status::],lstSectInline+lstNoData .99:EndProcedure PseudoGROUP
PseudoSTRUC Procedure Stm StrucNameBuf LocalVar Ii LocalVar Size=SIZE#II ; Temporary structire II used for StmGetIiModifiers. ClearLocalVar MOV EBX,[%Stm] JNSt [EBX+STM.CtxStatusAll],ctxNoEmit,.20: StackPush [Src.CtxStack::],0 ; In noemitting state only create ctxSTRUC. Invoke CtxCreate::,EAX,ctxSTRUC+ctxNamespace+ctxNoEmit,EBX JMP .90: .20:Invoke StmCheckFields::,EBX,"1001" JC .90: Invoke EaBufferReserve::,PseudoSTRUC MOV [%StrucNameBuf],EAX Invoke SymDelocalName::,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EAX,memberDelocal JC .80: BufferRetrieve EAX ; ESI,ECX is now delocalized structure name. Check for duplicate STRUC declaration. Invoke SssFindByName::,sssStructure,0,ESI,ECX,0 JC .50: ; Jump when OK, not found yet. SUB ECX,ECX ; An existing structure EAX is redeclared, erase .Org and old members. MOV [EAX+SSS.OrgLow],ECX MOV [EAX+SSS.TopLow],ECX BufferClear [EAX+SSS.EmitBuffer] BufferClear [EAX+SSS.SssOrdBuffer] JNSt [EAX+SSS.Status],sssDefinedInPass,.60: ; Found but it was created in previous pass. This is OK. Msg '7834',EAX,[EAX+SSS.LinePtr] ; Structure "!1S" was already declared in !2@. JMP .80: .50:LEA EDI,[%Ii] Invoke StmGetIiModifiers::,EBX,EDI MOV EDX,[EDI+II.Align] ; EDX is explicit alignment, or 0. MOV EAX,[EBX+STM.Program] ; Otherwise use program width as the default structure alignment. MOV EAX,[EAX+PGM.Pgmopt+PGMOPT.Status] AND EAX,pgmoptWidthMask ; Also synchronized with sssWidthMask. TEST EDX JNZ .58: ; Skip when ALIGN= was explicitly specified. MOV DL,2 ; Otherwise set structure alignment depending on program width. JSt EAX,sssWidth16,.58: MOV DL,8 JSt EAX,sssWidth64,.58: MOV DL,4 .58:OR EAX,sssStructure+sssDefinedInPass Invoke SssCreateStructure::,EBX,ESI,ECX,EAX,EDX JC .80: .60:MOV EDI,EAX ; ^SSS. StackPush [Src.CtxStack::],0 Invoke CtxCreate::,EAX,ctxSTRUC+ctxDefinition+ctxNamespace, EBX MOV [EAX+CTX.ObjPtr],EDI SetSt [Src.Lst.Status::],lstSectInline ; Display [Segm] in dump column on the same line. MOV EDX,[EBX+STM.Program] MOV [EBX+STM.Section],EDI ; Change current section to the new structure. MOV ECX,[EDX+PGM.CurrentSect] ; Section to return to after ENDSTRUC (in case of structure definition). MOV [EAX+CTX.PreviousSect],ECX .80:Invoke EaBufferRelease::,[%StrucNameBuf] .90:EndProcedure PseudoSTRUC
PseudoENDSTRUC Procedure Stm MOV EBX,[%Stm] Invoke CtxFind::,ctxSTRUC,EBX Msg cc=C,'7120',Dict_PseudoENDSTRUC:: ; Wrong nesting, unexpected !1S. JC .90: Msg cc=NE,'7110',Dict_PseudoENDSTRUC::,EAX ; Wrong nesting, expected "!1S !2S". JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.60: MOV ECX,[EAX+CTX.PreviousSect] ; Section before entering STRUC/ENDSTRUC block. MOV [EBX+STM.Section],ECX ; Tell StmFlush to change the section in Pgm. .60:SetSt [Src.Lst.Status::],lstSectKeep ; Do not display the change in this listing line yet. MOV ESI,[EAX+CTX.ObjPtr] ; ^SSS - ending structure. Invoke CtxDiscard::,EAX,EBX JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: Invoke StmCheckFields::,EBX,"00?0" ; If it's the final pass and statement is not in included source, warn when the structure was never used. TEST ESI JZ .90: JSt [ESI+SSS.Status],sssUsed,.90: JSt [EBX+STM.Status],stmIncluded,.90: MOV EDX,[EBX+STM.Program] JNSt [EDX+PGM.Status],pgmLastPass,.90: Msg PgmStatus=pgmLastPass,'2102',ESI ; Structure !1S was defined but never used. .90:EndProcedure PseudoENDSTRUC
PseudoEQU Procedure Stm EquExp LocalVar Size=SIZE#EXP ; EXP of the operand. EquVal LocalVar Size=8 ; Hexadecimal value of symbol, big endian. MOV EBX,[%Stm] SetSt [Src.Lst.Status::],lstEQU JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: Invoke StmCheckFields::,EBX,"1010" ; Label and 1 operand is mandatory. JC .90: BufferRetrieve [EBX+STM.OrdBuffer] MOV ECX,[ESI+4] MOV ESI,[ESI+0] LEA EDI,[%EquExp] Invoke ExpEval::,EDI,ESI,ECX,EBX ; Evaluate the ordinal operand to expression in EDI. SUB ECX,ECX ; Test the special case when assigned symbol is $. Invoke StmCheckLabel$::,EBX JNE .20: ; Skip when the label is not $. INC ECX ; ECX=1 if assigned symbol is $. ECX=0 otherwise. .20: MOV EAX,[EDI+EXP.Status] Dispatch AL,'N','A','G',0x23 .E6631:Msg '6631' ; Number or address expected. .0x23:MOV EAX,[EBX+STM.OffsetLow] ; Invalid value will be treated as $ (address of the current origin). MOV EDX,[EBX+STM.OffsetHigh] MOV ESI,[EBX+STM.Section] JMP .30: .G: Invoke ExpConvertToNumber::,EDI ;Symbol EQU CharConst
. JC .E6631: .N: ;Symbol EQU scalar
. .A: MOV EAX,[EDI+EXP.Low] ;Symbol EQU address
. MOV EDX,[EDI+EXP.High] MOV ESI,[EDI+EXP.Seg] .30:JECXZ .60: ; Skip when the assigned symbol is not $. TEST ESI JNZ .35: MOV ESI,[EBX+STM.Section] ; If$ EQU scalar
, it is assumed to be an offset from the current section. .35: CMP ESI,[EBX+STM.Section] ;$ EQU Address
must be in the current section. JE .40: Msg '6634' ; Origin offset "$" may be equated only within the current section or structure. JMP .0x23: .40:MOV ESI,[EBX+STM.Section] SUB EAX,[ESI+SSS.OrgLow] SBB EDX,[ESI+SSS.OrgHigh] TEST EDX ; EDX may be 0 or -1 only. JZ .50: INC EDX JZ .50: Msg '6635' ; Origin offset "$" cannot be equated more than 4 GB away. .50:MOV [EBX+STM.AlignBytes],EAX JMP .90: .60:SUB ECX,ECX ; Assigned symbol is not "$". It may be in any section or plain number. MOV CL,[EDI+EXP.Status] MOV [EBX+STM.Status],CL ; Symbol type is copied from the expression type. MOV [EBX+STM.OffsetLow],EAX MOV [EBX+STM.OffsetHigh],EDX ; Prepare symbol value for listing. Its section will be in Lst.Section, its value goes to Lst.SetBuffer. ; Displayed width (16,32,64) corresponds with current section width but it may be increased if necessary. MOV [Src.Lst.Section::],ESI MOV EDI,[EBX+STM.Program] MOV ESI,sssWidth16 ; Default section width if Pgm is not set. TEST EDI JZ .70: MOV EDI,[EDI+PGM.CurrentSect] TEST EDI JZ .70: MOV ESI,[EDI+SSS.Status] .70:LEA EDI,[%EquVal] Invoke ExpWidth:: ; CL=magnitude of the number in EDX:EAX. Dispatch CL,expWidth8B,expWidth4B .expWidth2B: JSt ESI,sssWidth32|sssWidth64,.expWidth4B: ; Listing of EQU symbol is DWORD in 32bit mode. XCHG AH,AL MOV [EDI],AX ; Listing of EQU symbol is WORD in 16bit mode. MOV CL,2 JMPS .80: .expWidth4B: JSt ESI,sssWidth64,.expWidth8B: ; Listing of EQU symbol is QWORD in 64bit mode. BSWAP EAX MOV [EDI],EAX MOV CL,4 JMPS .80: .expWidth8B: BSWAP EDX MOV [EDI],EDX BSWAP EAX MOV [EDI+4],EAX MOV CL,8 .80:; Symbol section is temporarily stored to Stm.Section but it will be restored from ESI after SymCreate, ; because EQU never changes the current section. MOV ESI,[Src.Lst.Section::] XCHG [EBX+STM.Section],ESI Invoke LstSet::,EDI,ECX ; EQUated symbol will be listed by its value. MOV EAX,stmLabelIsPublic AND EAX,[EBX+STM.Status] OR EAX,symDefined Invoke SymCreate::,EAX,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EBX MOV [EBX+STM.Section],ESI ; Leave the current section as it was before PseudoEQU. .90:EndProcedure PseudoEQU
PseudoINCLUDE Procedure Stm MOV EBX,[%Stm] Invoke ChunkInclude::,EBX EndProcedure PseudoINCLUDE
PseudoINCLUDE1 Procedure Stm MOV EBX,[%Stm] SetSt [EBX+STM.Status],stm1 Invoke ChunkInclude::,EBX EndProcedure PseudoINCLUDE1
PseudoINCLUDEHEAD Procedure Stm MOV EBX,[%Stm] SetSt [EBX+STM.Status],stmIncludeHead Invoke ChunkInclude::,EBX EndProcedure PseudoINCLUDEHEAD
PseudoINCLUDEHEAD1 Procedure Stm MOV EBX,[%Stm] SetSt [EBX+STM.Status],stm1+stmIncludeHead Invoke ChunkInclude::,EBX EndProcedure PseudoINCLUDEHEAD1
PseudoINCLUDEBIN Procedure Stm MOV EBX,[%Stm] SetSt [EBX+STM.Status],stmIncludeBin Invoke ChunkInclude::,EBX EndProcedure PseudoINCLUDEBIN
PseudopcERROR Procedure Stm MsgId LocalVar ; '5000' decadic ID. MsgText LocalVar Size=8 MOV EBX,[%Stm] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: Invoke StmCheckFields::,EBX,"00*?" JC .90: SetSt [Src.Lst.Status::],lstNoData ; Get message Id. SUB EDX,EDX BufferRetrieve [EBX+STM.KeyBuffer] TEST ECX JZ .20: ; No ID= specified, use default 5000. ; Only one keyword accepted: ID=. CMPD [ESI+4],2 ; Keyname size. JNE .W3870: MOV EAX,[ESI+0] ; Keyword name pointer. MOV AX,[EAX] ; Two characters of keyword. AND AX,0xDFDF ; Convert them to uppercase. CMP AX,'ID' JE .10: .W3870: Msg '3870',ESI ; Ignored illegal %%ERROR option "!1S". Expected ID=. JMPS .40: .10:Invoke ExpEvalNum::,[ESI+8],[ESI+12] JC .20: TEST EDX JNZ .W3872: CMP EAX,5999 JA .W3872: CMP EAX,5000 JAE .30: ADD EAX,5000 CMP EAX,5999 JA .W3872: CMP EAX,5000 JAE .30: .W3872:Msg '3872',EAX ; Wrong %%ERROR ID=!1D, expected 0..999 or 5000..5999. .20: MOV EAX,5000 ; Default ID when wrong or none provided. .30: ADD ESI,16 ; Next keyword, if any. CMP ECX,16 JA .W3870: .40: LEA EDI,[%MsgId] StoD EDI,Size=4 BufferRetrieve [EBX+STM.OrdBuffer] JCXZ .70: Invoke EaBufferReserve::,PseudopcERROR MOV EDX,EAX .50: BufferStore EDX,[ESI+0],[ESI+4] ; Concatenate %ERROR comma-separated ordinals to one line of text. BufferStoreByte EDX,',' ADD ESI,8 SUB ECX,8 JA .50: BufferDecrement EDX ; Remove the last comma. BufferRetrieve EDX Invoke EaBufferRelease::,EDX .70: LEA EDI,[%MsgText] MOV [EDI+0],ESI MOV [EDI+4],ECX Msg [%MsgId],EDI .90:EndProcedure PseudopcERROR
PseudoLINK Procedure Stm MOV EBX,[%Stm] StmInStruc EBX,.90: ; Do not allow this statement in a structure definition. SetSt [Src.Lst.Status::],lstNoData JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: Invoke StmCheckFields::, EBX,"00*0" MOV EDX,[EBX+STM.Program] TEST EDX JZ .90: ; Unexpected behaviour. JNSt [EDX+PGM.Status],pgmLastPass,.90: ; Instruction LINK is processed in final pass only. BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .90: ; If no operands. .20: LODSD MOV EDI,EAX LODSD ; EDI,EAX is now a string with filename/filemask. PUSH ECX,ESI MOV ECX,[EDX+PGM.LinkFilesNr] MOV ESI,[Ea.Eaopt.MaxLinks::] CMP ECX,ESI JB .30: Msg '7703',ESI ; Number of linked files exceeded MaxLinks=!1D. POP ESI,ECX JMP .90: ; Abort further linking when MaxLinks exceeded. .30: MOV ESI,EDI MOV ECX,EAX StripSpaces ESI,ECX StripQuotes ESI,ECX JECXZ .80: INC ECX ; Room for zero terminator. PoolNew [EDX+PGM.Pool],ECX,Align=Byte MOV EDI,EAX DEC ECX REP MOVSB MOV ESI,EAX SUB EAX,EAX STOSB MOV ECX,[EDX+PGM.LinkFilesNr] MOV EDI,[EDX+PGM.LinkFileNamesTable] MOV [EDI+4*ECX],ESI MOV EDI,[EDX+PGM.LinkLinePtrTable] MOV EAX,[EBX+STM.LinePtr] MOV [EDI+4*ECX],EAX INC ECX MOV [EDX+PGM.LinkFilesNr],ECX .80:POP ESI,ECX SUB ECX,8 JA .20: .90:EndProcedure PseudoLINK
PseudoScope Procedure Statement, Scope DllNameSize LocalVar DllNamePtr LocalVar FwdNameSize LocalVar FwdNamePtr LocalVar ClearLocalVar MOV EBX,[%Statement] MOV EDX,[%Scope] JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.90: SetSt [Src.Lst.Status::],lstNoData Invoke StmCheckFields::, EBX,"*0**" ; Warn if machine prefix is present in statement. JNSt EDX,symImport|symExport,.40: ; IMPORT and EXPORT may specify dynamic library LIB=. BufferRetrieve [EBX+STM.KeyBuffer] JECXZ .40: .10:; EXPORT may specify forward symbol FWD= and its dynamic library LIB=. Invoke DictLookup::,DictDynLinkKeys::,[ESI+0],[ESI+4] ; Only FWD= and LIB= is expected. Dispatch AL,dictKeyLIB,dictKeyFWD .W3866:Msg '3866',ESI ; Ignored illegal option "!1S=". JMP.30: .dictKeyLIB: JNSt EDX,symImport|symExport,.W3866: MOV EDI,[ESI+8] ; New LIB= valuePtr. MOV EAX,[ESI+12] ; New LIB= valueSize. StripQuotes EDI,EAX ; Quotes are optional. MOV [%DllNamePtr],EDI MOV [%DllNameSize],EAX JMP .30: .dictKeyFWD: JNSt EDX,symExport,.W3866: MOV EDI,[ESI+8] ; New FWD= valuePtr. MOV EAX,[ESI+12] ; New FWD= valueSize. MOV [%FwdNamePtr],EDI MOV [%FwdNameSize],EAX .30:ADD ESI,16 ; Inspect other keywords, if any, to detect unexpected keys. SUB ECX,16 JG .10: ; The next keyword operand. .40:JNSt [EBX+STM.Status],stmLabelPresent, .55: ; Declare symbol with scope EDX from the label field. .50:Invoke SymCreate::,EDX,[EBX+STM.LabelPtr],[EBX+STM.LabelSize],EBX Invoke SymDynamicLink::,EAX,[EBX+STM.Program],[%DllNamePtr],[%DllNameSize], \ [%FwdNamePtr],[%FwdNameSize] .55:BufferRetrieve [EBX+STM.OrdBuffer] JECXZ .90: .60:; Declare symbol with scope EDX from the ordinal ESI field. Invoke SymCreate::,EDX,[ESI+0],[ESI+4],EBX Invoke SymDynamicLink::,EAX,[EBX+STM.Program],[%DllNamePtr],[%DllNameSize], \ [%FwdNamePtr],[%FwdNameSize] .80: ADD ESI,8 SUB ECX,8 JA .60: ; The next ordinal operand. .90:EndProcedure PseudoScope
PseudoGLOBAL Procedure Stm Invoke PseudoScope,[%Stm],symGlobal EndProcedure PseudoGLOBAL
PseudoPUBLIC Procedure Stm Invoke PseudoScope,[%Stm],symPublic EndProcedure PseudoPUBLIC
PseudoEXTERN Procedure Stm Invoke PseudoScope, [%Stm],symExtern EndProcedure PseudoEXTERN
PseudoEXPORT Procedure Stm Invoke PseudoScope,[%Stm],symExport ; +stmPublic EndProcedure PseudoEXPORT
PseudoIMPORT Procedure Stm Invoke PseudoScope, [%Stm],symImport EndProcedure PseudoIMPORT
ENDPROGRAM pseudo