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.
pseudo PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32,MAXPASSES=64
INCLUDEHEAD euroasm.htm, \ Interface (structures, symbols and macros) of other modules used in this source.
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
pseudo HEAD
; This modul has no interface.
ENDHEAD pseudo
[.text]
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.
TEST ESI
JZ .90: ; When the current segment was not established yet.
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:stmtNotBSSflag. 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]
PUSH ESI
MOV ESI,EDI
CALL PseudopcDISPLAY.ForwardSlash:
POP ESI
Msg '1160',EDI,[ESI+FILE.Size],EDX ; "!1$",size=!2K,src=!3$.
MOV ESI,Ea.SrcFile:: ; Main source file.
LEA EDI,[ESI+FILE.Name]
PUSH ESI
MOV ESI,EDI
CALL PseudopcDISPLAY.ForwardSlash:
POP ESI
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]
PUSH EAX,ESI
MOV ESI,EAX
CALL PseudopcDISPLAY.ForwardSlash:
POP ESI,EAX
Msg '1180',EAX,[ESI+FILE.Size],EDX ; "!1$",size=!2K,src=included in !3@.
INC ECX
JMP .F3:
.F9: RET
ENDP PseudopcDISPLAY.FILES::
PseudopcDISPLAY.ForwardSlash: PROC ; Convert backslash from ASCIIZ string at ESI to forward slash.
PUSH EAX,ESI,EDI
MOV EDI,ESI
.S2: LODSB
CMP AL,0
JE .S8:
CMP AL,'\'
JNE .S5:
MOV AL,'/'
.S5: STOSB
JMP .S2:
.S8: POP EDI,ESI,EAX
RET
ENDP PseudopcDISPLAY.ForwardSlash:
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]
CALL PseudopcDISPLAY.ForwardSlash:
.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.
PUSHAD
ADD EDX,EDI
DEC EDI
.I2: INC EDI
CMP EDI,EDX
JNB .I4:
CMPB [EDI],'\'
JNE .I2:
. MOVB [EDI],'/'
JMP .I2:
.I4:POPAD
MOV [EBX+0],EDI
MOV [EBX+4],EDX
JNSt [EAX+CHUNK.Status],chunkError,.I6:
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 .I5:
MOV AL,'E'
.I5: STOSB
MOV EAX,EDX
StoD EDI,Size=4,Align=right,LeadingZeroes=yes
XOR EAX,EAX
STOSB
POP EDI,EAX
JMP .I7:
.I6:MOV EDI,=B"orig"
JSt [EAX+CHUNK.Status],chunkOrig|chunkOrigBin,.I7:
MOV EDI,=B"binary"
JSt [EAX+CHUNK.Status],chunkBin,.I7:
MOV EDI,=B"source"
JNSt [EAX+CHUNK.Status],chunkResolved|chunkSkipped,.I7:
MOV EDI,=B"resolved"
JNSt [EAX+CHUNK.Status],chunkSkipped,.I7:
MOV EDI,=B"skipped"
.I7: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*
; Convert 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]
TEST ECX
JZ .90:
MOV ECX,[ECX+PGM.PassPtr]
TEST ECX
JZ .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: MOV EDI,[ESI] ; ESI points to Ptr,Size of ordinal.
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.
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 (the 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
CMP EAX,[EDI+PGM.MemPeakPass] ; EAX=memory allocated by the pass.
JNA .75:
MOV [EDI+PGM.MemPeakPass],EAX
.75: Invoke PgmLinkSections::,EDI
Invoke PgmDestroy::, EDI,EBX ; Combine and link modules and store the output file.
CMP EAX,[Src.MemPeakPgm::]
JNA .80:
MOV [Src.MemPeakPgm::],EAX
.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: ; This is unexpected behaviour.
JNSt [EDX+PGM.Status],pgmLastPass,.90: ; Instruction LINK is processed in final pass only.
BufferRetrieve [EBX+STM.OrdBuffer]
TEST ECX
JZ .90: ; If no operands.
.20: LODSD ; Load a pointer to the next operand.
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: ; ESI,ECX will be popped at .80:.
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