EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

sss.htm
Class
SSS
Enumeration
SssPurposeList
Encodings
SssEnc
SssPurposeEnc
Procedures
SssCombine
SssCreate
SssCreate@LT
SssCreate@RT
SssCreateExtern
SssEmit
SssEmitAlignment
SssFind
SssGetCoffCharacteristics
SssGetSegm
SssGuessPurpose
SssCheckDirty
SssCheckPurpose
SssLinkSection
SssLinkSegment
SssPurposeToText
SssResizeGroup

SSS is a common class for objects of five types: group, segment, section, structure, external-pseudosegment.
SSS objects are stored on Pgm.SssList.
Flags in SSS.Status::sssTypeMask specify which type the object actually represents.

sssSegment and sssSection may be set simultaneously and such object represents both segment and its base section. Base section is the one with the same name as its hosting segment, its address is identical with the first byte of the segment.

sssGroup and sssSegment may not be set simultaneously (even when it is base segment of the group), although they may have identical names. Base segment of the group is the one which was declared first (compared to other members of the group) and has therefore the lowest VA.

Flag sssExtern identifies the object as an auxilliary pseudosegment which belongs to external symbol mentioned in program. Each external symbol has its own unique pseudosegment. No other flag in sssTypeMask is set together with sssExtern.

Default implicit segments are created at PgmCreateProgram by PgmCreateImplicitSegments for each purpose (code,data,bss,stack), actual default segment names depend on the program format and model. Other SSS object may be created with explicit STRUC, GROUP, SEGMENT, [section], EXTERN statements, and the unused default segments will be abandoned in this case.

Structures are handled semistatically, SSS object is created or updated in PseudoSTRUC, where all its buffers and properties are cleared, except for SSS.Name and SSS.Top. Then it is rebuild from D* statements again in each pass. SSS structure is not cleared on pass transition, which allows to forward reference structured data in source and declare STRUC/ENDSTRUC block later.

Tree of program groups, segments and sections (but not external pseudosegments) is maintained with members .SegmPtr and .GroupPtr and it can be inspected using the statement %DISPLAY with operand GROUP, SEGMENT or SECTION (all operands display the same tree).


sss PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
 sss HEAD  ; Start of module interface.
↑ SSS
Virtual size of the structure/section/segment/group is .Top - .Bottom . It is not necessarily equal to .EmitBuffer size.
Object types sssSection and sssSegment may be set simultaneously in one object. Types sssGroup, sssStructure and sssExtern are exclusive.
Member .Bottom represents virtual address of the structure/section/segment/group.
.Status:sssNotBSS is set when the object contains initialized data. In the case of structure it is set when at least one structure member was defined with initialized data value, so the instances of the structure will be placed is section with PURPOSE=DATA if AUTOSEGMENT=ENABLED.
SSS pointers at assembly time
Object type.Bottom.GroupPtr.SegmPtr
sssStructure000
sssSectionsection VA0 or ^explicit group^segment
sssSegment00 or ^explicit group^self
sssGroup0^self0 or ^base segment
sssExtern00^extern
SSS pointers at link time
Object type.Bottom.GroupPtr.SegmPtr
sssStructure000
sssSectionN/AN/AN/A
sssSegmentsegment VA^explicit or implicit group^self
sssGroupgroup VA^self^base segment
sssExternextern VA^explicit or implicit group^extern
SSS STRUC         ; +00h.
.NamePtr      D D ; Pointer to object name without []. In case of sssExtern it is the symbol name.
.NameSize     D D ; Size of object name.
.ClassPtr     D D ; CLASS= property of segment. Unquoted identifier.
.ClassSize    D D ; Netto size of segment property CLASS= without quotes.
                  ; +10h.
.Status       D D ; Boolean properties of the object in SssEncoding.
.Purpose      D D ; Purpose of the section or segment in SssPurposeEncoding.
.SegmIndex    D D ; Index of this segment in COFF SectionTable (1..)/OMF SGMDEF ordinal/GRPDEF record (1..).
.NameIndex    D D ; Index of this segment in COFF SymbolTable (0..) /OMF LNAMES ordinal (1..).
                  ; +20h.
.BottomLow    D D ; Base offset of section in segment. Greater than 0 in non-base sections only.
.BottomHigh   D D ;     Bottom is 0 at assembly time for structures, segments, groups.
.OrgLow       D D ; Origin pointer {offset of $}. Reset to .Bottom at start of each pass.
.OrgHigh      D D ;
                  ; +30h.
.TopLow       D D ; Used limit of section/segment. Maximum of .Org ever reached. Not reset at start of pass.
.TopHigh      D D ; Used limit of section/segment. Maximum of .Org ever reached.
              D D ; Unused.
.Alignment    D D ; 0=unspec 1=B,2=W,4=D,8=Q,16=O,32=Y,64=Z,128,256 etc.
                  ; +40h.
.LinePtr      D D ; Physical line which declared the object.
.SymPtr       D D ; Ptr to a public SYM when sssExtern was resolved in PgmLinkImage.
.GroupPtr     D D ; Ptr to group object (another SSS structure) which this segment/group belongs to.
.SegmPtr      D D ; Ptr to segment object (another SSS structure) which this section belongs to.
                  ; +50h.
.PgmPool      D D ; Pointer to POOL where structure-member names will be stored persistently.
.EmitBuffer   D D ; Pointer to BUFFER with emitted code/data.
.RelocBuffer  D D ; Pointer to BUFFER with RELOC records.
.MemberBuffer D D ; Pointer to BUFFER with MEMBER records. Used with sssStructure.
  ENDSTRUC SSS
↑ SssEnc
Encoding of flags used in SSS.Status.
                  ; SSS type specifies the kind of SSS object.
sssStructure      EQU 0x0000_0001 ; Object is a structure.
sssSection        EQU 0x0000_0002 ; Object is a section of segment.
sssSegment        EQU 0x0000_0004 ; Object is a segment. 
sssGroup          EQU 0x0000_0008 ; Object is a group of segments. 
sssExtern         EQU 0x0000_0010 ; Object is an external pseudosegment. 
sssTypeMask       EQU sssStructure|sssExtern|sssGroup|sssSegment|sssSection
                  ; Auxilliary properties of SSS object.
sssImport         EQU 0x0000_0020 ; External pseudosegment belongs to an imported symbol.
sssNotBSS         EQU 0x0000_0800 ; The object is initialized.
                  ; COMBINE= property of segment.
sssPublic         EQU 0x0000_1000 ; Segment will be combined with other segments with the same name.
sssPrivate        EQU 0x0000_2000 ; Segment must not be combined with others.
sssCommon         EQU 0x0000_4000 ; Segment may be overlaped.
sssStack          EQU 0x0000_8000 ; Segment may be concatenated.
sssCombineMask    EQU sssPublic|sssPrivate|sssCommon|sssStack
                  ; Attributes of external symbols postponed to link time.
sssExtAttr        EQU 0x000F_0000 ; dictAttr* (0..9) <<16. Sync with relocExtAttr.
                  ; WIDTH= property of segment. Encoding must match pgmoptWidthMask.
sssWidth16        EQU 0x0100_0000 ; Realmode 16bit program segment.
sssWidth32        EQU 0x0200_0000 ; Realmode or protected 32bit program segment.
sssWidth64        EQU 0x0400_0000 ; 64bit mode program segment.
sssWidthMask      EQU sssWidth16|sssWidth32|sssWidth64 ; This must match pgmoptWidthMask.
                  ; Miscellaneous properties.
sssOrgEqu         EQU 0x0800_0000 ; Just emitted statement was $ EQU something. Used in SssEmit.
sssDefinedInPass  EQU 0x1000_0000 ; Object was already declared in this pass. Reset in PassCreate.
sssDefinedInGroup EQU 0x2000_0000 ; Object was declared in GROUP pseudoinstruction.
sssImplicit       EQU 0x4000_0000 ; Group/segment was created by default in PgmCreateProgram.
sssUsed           EQU 0x8000_0000 ; Structure/section/segment was referred at least once (sssDirty).
↑ SssPurposeList
enumerates names of possible segment purposes. Order of the first sixteen names ( EXPORT .. RESERVED) must match the order of indexes in PFPE_encodings, i.e. the order of special data directories in optional header.
General purposes follow, see SssPurposeEnc.
Some of the names can be aliased, see DictSegmentPurpose.
%SssPurposeList %SET                                                                 \
        EXPORT, IMPORT, RESOURCE, EXCEPTION, SECURITY, BASERELOC, DEBUG, COPYRIGHT,  \
        GLOBALPTR, TLS, LOAD_CONFIG, BOUND_IMPORT, IAT, DELAY_IMPORT, CLR, RESERVED, \
        CODE, DATA, BSS, STACK, LITERAL, DRECTVE
↑ SssPurposeEnc
Encoding of flags used in SSS.Purpose. It is based on the order specified in SssPurposeList above.
sssPurposeEXPORT = 0x0000_0001 sssPurposeIMPORT = 0x0000_0002 sssPurposeRESOURCE = 0x0000_0004 sssPurposeEXCEPTION = 0x0000_0008 sssPurposeSECURITY = 0x0000_0010 sssPurposeBASERELOC = 0x0000_0020 sssPurposeDEBUG = 0x0000_0040 sssPurposeCOPYRIGHT = 0x0000_0080 sssPurposeGLOBALPTR = 0x0000_0100 sssPurposeTSL = 0x0000_0200 sssPurposeLOAD_CONFIG = 0x0000_0400 sssPurposeBOUND_IMPORT = 0x0000_0800 sssPurposeIAT = 0x0000_1000 sssPurposeDELAY_IMPORT = 0x0000_2000 sssPurposeCLR = 0x0000_4000 sssPurposeRESERVED = 0x0000_8000 sssPurposeCODE = 0x0001_0000 sssPurposeDATA = 0x0002_0000 sssPurposeBSS = 0x0004_0000 sssPurposeSTACK = 0x0008_0000 sssPurposeLITERAL = 0x0010_0000 sssPurposeDRECTVE = 0x0020_0000
 %Value %SETA 1
 PURPOSE %FOR %SssPurposeList
    sssPurpose%PURPOSE EQU %Value
    %Value %SETA %Value << 1                                               ; >>
  %ENDFOR PURPOSE
    ; The first 16 purposes specify .DataDirectory in PE optional header:
sssPurposeOptionalMask EQU 0x0000_FFFF ; EXPORT,IMPORT,RESOURCE,...
    ; Segments with initialized contents:
sssPurposeInitMask EQU sssPurposeOptionalMask | sssPurposeCODE | sssPurposeDATA
  ENDHEAD sss ; End of module interface.
↑ SssFind SssType, SssCombine, NamePtr, NameSize, ProgPtr
Procedure SssFind will search for an SSS object with given SssType and SssCombine by its Name.
Input
SssType One or more flags sssExtern, sssGroup, sssSegment, sssSection, sssStructure . or 0 (any type of SSS objects indulges).
SssCombine One on more flags sssPrivate,sssPublic,sssCommon,sssStack or 0 (any combine mask indulges).
NamePtr Pointer to searched name without [ ].
NameSize Number of bytes in the searched name.
ProgPtr is pointer to PGM whose segments are searched. It may be NULL, current program is searched then.
Output
CF=0, EAX= pointer to SSS object.
Error
CF=1, EAX=0 if no such object was found.
Example
Invoke SssFind sssStructure, 0, ESI, ECX, 0
Invokes
PgmGetCurrent
Invoked by
ExpEval ExpEvalIdentifier ExpParseDatatype PfDrectveCreate PfDrectveDestroy PfcoffLoadModule PfmzDefaultStack PgmListLiterals PseudoGROUP PseudoNoOperation PseudoSEGMENT PseudoSTRUC SssCombine SssCreate SssCreate@LT SssCreate@RT SssCreateExtern SssLinkSegment
SssFind Procedure SssType, SssCombine, NamePtr, NameSize, ProgPtr
    MOV EAX,[%ProgPtr]
    TEST EAX
    JNZ .10:
    Invoke PgmGetCurrent::
    JC .90:
.10:MOV EAX,[EAX+PGM.SssList]
    MOV EDX,[%SssType]
    MOV ECX,[%SssCombine]
    ListGetFirst EAX
.30:STC
    JZ .90:
    TEST EDX
    JZ .40: ; Do not constrain object type when none was specified.
    JNSt [EAX+SSS.Status],EDX,.80: ; Skip if object type does not match.
.40:JECXZ .70: ; Do not constrain combine method when none was specified.
    JNSt [EAX+SSS.Status],ECX,.80: ; Skip if combine method does not match.
.70:Compare [%NamePtr],[%NameSize],[EAX+SSS.NamePtr],[EAX+SSS.NameSize]
    JE .90:
.80:ListGetNext EAX
    JMP .30:
.90:MOV [%ReturnEAX],EAX
  EndProcedure SssFind
↑ SssCheckDirty SssPtr, PgmPtr
SssCheckDirty inspects the group, segment or section if it was referred or if something was emitted to it.
Section is dirty if something was emitted to it or if at least one address symbol was defined in it.
Segment is dirty if any of its section is dirty.
Group is dirty if at least one segment belongs to it.
Structure is never dirty.
Input
SssPtr Pointer to the inspected segment or section.
PgmPtr Pointer to the program which the segment belongs to.
Output
CF=0 if empty, 1 if dirty (some code, data or label was emitted).
Invoked by
PassDestroy PgmCheckDirty PseudoSEGMENT
SssCheckDirty Procedure SssPtr, PgmPtr
      MOV EBX,[%SssPtr]
      MOV EDI,[%PgmPtr]
      MOV EDX,[EBX+SSS.Status]
      JSt EDX,sssStructure, .NotDirty:
      JSt EDX,sssUsed, .Dirty:
      JNSt EDX,sssSection,.55:
      ; Section is dirty if any data was reserved in it.
      MOV EAX,[EBX+SSS.TopLow]
      OR  EAX,[EBX+SSS.TopHigh]
      JNZ .Dirty:
      ; Section is dirty if any data was emitted in it.
      MOV ECX,[EBX+SSS.EmitBuffer]
      JECXZ .40:
      BufferRetrieve ECX
      JECXZ .40: ; If no data was emitted to this section.
.Dirty:SetSt [EBX+SSS.Status],sssUsed ; Mark as used, thus next time SssCheckDirty will know faster.
      STC
      JMP .90:
.40:  ; Section is dirty if any symbols belongs to it.
      ListGetFirst [EDI+PGM.SymList]
      JZ .55: ; If nothing was emitted to section EBX.
.45:  MOV ECX,[EAX+SYM.Section]
      JECXZ .50:
      CMP ECX,EBX
      JE .Dirty:
      MOV ECX,[ECX+SSS.SegmPtr]
      JECXZ .50:
      CMP ECX,EBX
      JE .Dirty:
.50:  ListGetNext EAX
      JNZ .45:
.55:  JNSt EDX,sssSegment,.70:
      ; Segment is dirty if any other section belongs to it beside its base section.
      ListGetFirst [EDI+PGM.SssList]
      JZ .70:
.60:  JNSt [EAX+SSS.Status],sssSection,.65:
      CMP [EAX+SSS.SegmPtr],EBX ; Does the section belong to inspected segment?
      JNE .65: ; If it does not belong to our segment.
      CMP EAX,EBX ; Is it the base section, identical with its segment?
      JNE .Dirty:
.65:  ListGetNext EAX
      JNZ .60:
.70:  JNSt EDX,sssGroup,.NotDirty:
      ; Group is dirty if any segment belongs to it.
      ListGetFirst [EDI+PGM.SssList]
      JZ .NotDirty:
.75:  JNSt [EAX+SSS.Status],sssSegment,.80:
      CMP [EAX+SSS.GroupPtr],EBX ; Does the segment belong to inspected group?
      JE .Dirty:
.80:  ListGetNext EAX
      JNZ .75:
.NotDirty:
      CLC
.90: EndProcedure SssCheckDirty
↑ SssGuessPurpose Segment
SssGuessPurpose will analyze the segment class and name and set the segment purpose according to a string case-insensitively found in the names, following theese rules:
  1. If the purpose is already set, procedure does nothing.
  2. Class name is checked first, then the segment name.
  3. If the name contains 'DRECTVE', sssPurposeDRECTVE is set.
  4. If the name contains 'STACK', sssPurposeSTACK is set.
  5. If the name contains 'BSS' or 'UDATA', sssPurposeBSS is set.
  6. If the name contains 'DATA', sssPurposeDATA is set.
  7. If the name contains 'CODE' or 'TEXT', sssPurposeCODE is set.
  8. Otherwise Segment.Purpose is left empty and ZF is returned.
Input
Segment is pointer to SSS structure with segment and class names already specified.
Output
ZF=0 Segment.Purpose is set.
Errors
ZF=1 if Segment.Purpose=0 (not detected).
See also
Manual.
Invoked by
PfcoffLoadModule PfomfLoadModule PseudoSEGMENT
Invokes
EaBufferRelease EaBufferReserve
SssGuessPurpose Procedure Segment
     MOV EBX,[%Segment]
     MOV ESI,[EBX+SSS.ClassPtr]
     MOV ECX,[EBX+SSS.ClassSize]
     CALL .Guess:
     MOV ESI,[EBX+SSS.NamePtr]
     MOV ECX,[EBX+SSS.NameSize]
     CALL .Guess:
.Guess:PROC1 ; Input: ESI,ECX is the name, EBX is the segment SSS.
     JECXZ .G99: ; If name empty.
     XOR EAX,EAX
     CMP [EBX+SSS.Purpose],EAX
     JNZ .G99: ; If purpose was already set.
     Invoke EaBufferReserve::, SssGuessPurpose
     MOV EDX,EAX
     BufferStore EDX,ESI,ECX
     BufferRetrieve EDX
     MOV EDI,ESI
.G01:LODSB    ; Convert the name to upper case.
     CMP AL,'a'
     JB .G05:
     CMP AL,'z'
     JA .G05:
     SUB AL,'a'-'A'
.G05:STOSB
     LOOP .G01:
     ; Search for string 'DRECTVE'.
     BufferRetrieve EDX
     MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G10:MOV AL,'D'
     REPNE SCASB
     JNE .G20:
     CMP ECX,6
     JB .G20:
     CMPD [EDI],'RECT'
     JNE .G10:
     CMPW [EDI+4],'VE'
     JNE .G10:
     MOV EAX,sssPurposeDRECTVE
     JMP .G90:
.G20:; Search for string 'STACK'.
     BufferRetrieve EDX
     MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G25:MOV AL,'S'
     REPNE SCASB
     JNE .G30:
     CMP ECX,4
     JB .G30:
     CMPD [EDI],'TACK'
     MOV EAX,sssPurposeSTACK ; If 'STACK' found in the name.
     JE .G90:
     JMP .G25:
.G30:; Search for string 'BSS'.
     BufferRetrieve EDX
     MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G35:MOV AL,'B'
     REPNE SCASB
     JNE .G40:
     CMP ECX,2
     JB .G40:
     CMPW [EDI],'SS'
     MOV EAX,sssPurposeBSS ; If 'BSS' found in the name.
     JE .G90:
     JMP .G35:
.G40:; Search for string 'UDATA'.
     BufferRetrieve EDX
     MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G45:MOV AL,'U'
     REPNE SCASB
     JNE .G50:
     CMP ECX,4
     JB .G50:
     CMPD [EDI],'DATA'
     MOV EAX,sssPurposeBSS ; If 'UDATA' found in the name.
     JE .G90:
     JMP .G45:
.G50:; Search for string 'DATA'.
     BufferRetrieve EDX
     MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G55:MOV AL,'D'
     REPNE SCASB
     JNE .G60:
     CMP ECX,3
     JB .G60:
     CMPD [EDI-1],'DATA'
     MOV EAX,sssPurposeDATA ; If 'DATA' found in the name.
     JE .G90:
     JMP .G55:
.G60:; Search for string 'CODE'.
     BufferRetrieve EDX
     MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G65:MOV AL,'C'
     REPNE SCASB
     JNE .G70:
     CMP ECX,3
     JB .G70:
     CMPD [EDI-1],'CODE'
     MOV EAX,sssPurposeCODE ; If 'CODE' found in the name.
     JE .G90:
     JMP .G65:
.G70:; Search for string 'TEXT'.
     BufferRetrieve EDX
     MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G75:MOV AL,'T'
     REPNE SCASB
     JNE .G80:
     CMP ECX,3
     JB .G80:
     CMPD [EDI-1],'TEXT'
     MOV EAX,sssPurposeCODE ; If 'TEXT' found in the name.
     JE .G90:
     JMP .G75:
.G80:XOR EAX,EAX ; No known purpose guessed.
.G90:SetSt [EBX+SSS.Purpose],EAX
     Invoke EaBufferRelease::,EDX
.G99:RET
     ENDP1 .Guess:
.90: XOR EAX,EAX
     CMP [EBX+SSS.Purpose],EAX ; Return ZF=1 if purpose was not guessed.
    EndProcedure SssGuessPurpose
↑ SssGetSegm PgmPtr, SegmentPurpose
SssGetSegm returns pointer to the first segment (in their declaration order) with given purpose.
It prefers used segments, only when no appropriate used segment exists, it may return unused (created by default) segment.
Input
PgmPtr Pointer to PGM object.
SegmentPurpose is required purpose in SssPurposeEnc.
Output
CF=0
EAX= Pointer to SSS segment object.
Error
CF=1 if no such segment found.
EAX=0
Example
Invoke SssGetSegm, EBX, sssPurposeDATA
Invoked by
IiAssemble PassCreate PfcomCompile PseudoData PseudoNoOperation PseudoPROC
SssGetSegm Procedure PgmPtr, SegmentPurpose
     MOV EDX,[%SegmentPurpose]
     MOV ECX,[%PgmPtr]
     SUB EAX,EAX
     JECXZ .80:
     ListGetFirst [ECX+PGM.SssList]
     JZ .80:
.20: JNSt [EAX+SSS.Status],sssSegment,.40: ; Skip sections and structures.
     JSt [EAX+SSS.Status],sssUsed,.30:
     JSt [EAX+SSS.Status],sssImplicit,.40: ; In the first pass avoid default segments.
.30: JSt [EAX+SSS.Purpose],EDX, .90:       ; If found used segment with purpose EDX.
.40: ListGetNext EAX
     JNZ .20:
     ListGetFirst [ECX+PGM.SssList]        ; Second pass of segment list.
.60: JNSt [EAX+SSS.Status],sssSegment,.70: ; Skip sections and structures.
     JSt [EAX+SSS.Purpose],EDX, .90:       ; If found a segment with purpose EDX.
.70: ListGetNext EAX
     JNZ .60:
.80: STC ; Not found.
.90: MOV [%ReturnEAX],EAX
     EndProcedure SssGetSegm
↑ SssCreate Stm, Segment, NamePtr,NameSize, SssStatus, Purpose, Alignment
SssCreate adds a new SSS record on Stm.Program.SssList and initializes its .Name, .LinePtr, .SegmPtr, .EmitBuffer, .RelocBuffer, .Status, .Purpose, .Alignment.
.Name is made program-persistent. LinePtr is taken from Stm.LinePtr.
.GroupPtr is itself (sssGroup), otherwise 0.
.SegmPtr is parent segment (if sssSection), itself (sssSegment), 0 (sssStructure|sssGroup).
When a structure is created, .MemberBuffer is allocated. and .SegmIndex set to IMAGE_SYM_ABSOLUTE.
When a section is created, its .Status is copied from parent segment.
Other properties are empty.
Input
Stm Statement which creates the object.
Segment is NULL or pointer to SSS segment which the (literal) section is created in. If 0, Stm.Program must specify PGM object which the segment/section/strucutre is created for.
NamePtr Pointer to object name (without []). Name may be volatile, it will be stored on program pool. It must be unique among other existing SSS objects. In case of sssStructure it must be unique among symbols and it may be local.
Group, section and segment name is not delocalized even when it starts with ..
NameSize Size of object name.
SssStatus Combination of flags which describe the created SSS. One of sssTypeMask must be set.
Purpose Flag(s) defined in SssPurposeEnc.
Alignment Alignment value (0,1,2,4,8,16...)
Output
CF=0
EAX= pointer to the created SSS object.
Errors
are reported with macro Msg.
See also
SssCreate@LT SssCreate@RT
Invokes
EaBufferRelease EaBufferReserve SssFind SymDelocalName SymFindByName
Invoked by
PfDrectveCreate PfmzDefaultStack PfpeBaserelocCreate PfpeExportCreate PfpeImportCreate PfrsrcLoadPgm PgmCreateImplicitSegments PseudoGROUP PseudoNoOperation PseudoSEGMENT PseudoSTRUC SssCreate@LT SssCreate@RT
SssCreate Procedure Stm, Segment, NamePtr, NameSize, SssStatus, Purpose, Alignment
     MOV EAX,[%Stm]
     MOV EBX,[EAX+STM.Program]
     ; SSS.Name.
     Invoke EaBufferReserve::, SssCreate
     MOV EDX,symDelocalNone ; Segment and section names will not be delocalized when begin with ..
     JSt [%SssStatus],sssGroup|sssSegment|sssSection|sssExtern,.10:
     MOV EDX,symDelocal ; Structure names beginning with . will be prefixed with current namespace.
.10: Invoke SymDelocalName::,[%NamePtr],[%NameSize],EAX,EDX
     BufferRetrieve EAX
     Invoke EaBufferRelease::,EAX
     MOV EDX,[%SssStatus]
     JNSt EDX,sssSegment,.20:
     Invoke SssFind,sssSegment,0,ESI,ECX,0 ; Created segment must be unique, unless it is implicit.
     JC .20:
     MOV EDI,EAX
     JSt EDX,sssImplicit,.25: ; Do not create new implicit segment when it already exists.
     MOV [%ReturnEAX],EAX
     Msg '7836',EAX,[EAX+SSS.LinePtr] ; Segment [!1S] was already defined in !2@.
     STC
     JMP .90:
.20: PoolStore [EBX+PGM.Pool],ESI,ECX ; Make the Name permanent during PGM lifetime.
     MOV ESI,EAX ; ESI,ECX is now nonvolatile SSS name.
     ListNew [EBX+PGM.SssList],Zeroed=yes ; Allocate room for the new object.
     JC .90:
     MOV EDI,EAX ; EDI is now the new (empty) SSS object.
     MOV [%ReturnEAX],EAX
     MOV [EDI+SSS.NamePtr],ESI
     MOV [EDI+SSS.NameSize],ECX
     ; SSS.Status.
.25: MOV [EDI+SSS.Status],EDX
     ; SSS.Purpose.
     MOV EAX,[%Purpose]
     MOV [EDI+SSS.Purpose],EAX
     ; SSS.Alignment.
     MOV EAX,[%Alignment]
     MOV [EDI+SSS.Alignment],EAX
     ; SSS.LinePtr.
     MOV ECX,[%Stm]
     MOV EAX,[ECX+STM.LinePtr]
     MOV [EDI+SSS.LinePtr],EAX
     ; Dynamic memory objects.
     MOV EAX,[EBX+PGM.Pool]
     MOV [EDI+SSS.PgmPool],EAX
     JNSt EDX,sssGroup,.30:
     ; A group of segments is created.
     MOV [EDI+SSS.GroupPtr],EDI
     JMP .90: ; Group does not store emitted data and relocations.
 .30:MOV ECX,256 ; Initial buffer size for structures.
     JSt EDX,sssStructure,.32:
     MOV ECX,16K ; Initial buffer size for segments and sections.
 .32:BufferCreate EAX,Size=ECX
     MOV [EDI+SSS.EmitBuffer],EAX
     BufferCreate [EBX+PGM.Pool],Size=ECX
     MOV [EDI+SSS.RelocBuffer],EAX
     JNSt EDX,sssStructure,.60:
     ; A structure is created.
     BufferCreate [EBX+PGM.Pool],Size=32*SIZE#MEMBER ; Initial estimated capacity for 32 members.
     MOV [EDI+SSS.MemberBuffer],EAX
     MOVD [EDI+SSS.SegmIndex],-1 ; pfcoffSYM_ABSOLUTE
    ; Just defined strucure name might have been forward referenced sooner and thus incorrectly have created a symbol.
     Invoke SymFindByName::,0,[EDI+SSS.NamePtr],[EDI+SSS.NameSize],0
     JC .50: ; If it's not the case.
     JNSt [EAX+SYM.Status],symDefined,.40:
     Msg cc=NE,'6612',EAX,[EAX+SYM.LinePtr] ; Cannot declare structure "!1S" when such symbol was declared at !2@.
 .40:; False created but not defined symbol EAX will be removed, because its name belongs to the structure.
     ListRemove [EBX+PGM.SymList],EAX
 .50:CLC
     JMP .90:
     ; SSS.SegmPtr.
 .60:MOV [EDI+SSS.SegmPtr],EDI ; In case of sssSegment it points to itself.
     JSt EDX,sssSegment,.90:
     ; A section is created, perhaps a literal one.
     MOV EDX,[%Segment]
     TEST EDX
     JNZ .80:
    ; If no explicit %Segment specified, the section will be created in the current segment of the program.
     MOV EAX,[EBX+PGM.CurrentSect] ; In case of sssSection .SegmPtr points to parent segment.
     MOV EDX,[EAX+SSS.SegmPtr] ; Parent segment.
 .80:MOV [EDI+SSS.SegmPtr],EDX
   ; Section inherits some of its properties from parent segment.
     MOV EAX,[EDX+SSS.ClassPtr]
     MOV ECX,[EDX+SSS.ClassSize]
     MOV [EDI+SSS.ClassPtr],EAX
     MOV [EDI+SSS.ClassSize],ECX
     MOV EAX,[EDX+SSS.Purpose]
     RstSt EAX,sssPurposeLITERAL ; This purpose is not inherited to section.
     MOV [EDI+SSS.Purpose],EAX
     MOV EAX,[EDX+SSS.Status]
     AND EAX,sssCombineMask+sssWidthMask ;  ~sssTypeMask
     OR [EDI+SSS.Status],EAX
 .90:EndProcedure SssCreate
↑ SssCheckPurpose SectPtr, Purpose
SssCheckPurpose emits warning when purpose of the statement does not match section property.
Input
SectPtr Pointer to SSS - section where the data will be emitted. Returns CF if SectPtr is NULL.
Purpose Flag(s) in SssPurposeEnc specifying the purpose of bytes emitted to the section.
Output
CF=0 Purpose match.
Error
CF=1 W3205, W3206 are reported with macro Msg.
Example
Invoke SssCheckPurpose, EBX, sssPurposeCODE
Invoked by
IiAssemble PseudoData PseudoPROC
SssCheckPurpose Procedure SectPtr, Purpose
      MOV EDX,[%Purpose]
      MOV EBX,[%SectPtr]
      TEST EBX
      JZ .10:
      TEST [EBX+SSS.Purpose],EDX
      JNZ .90: ; If purpose matches, do nothing.
      JSt EDX,sssPurposeBSS,.90: ; Uninitialized data may be emitted anywhere, do nothing.
      JNSt [EBX+SSS.Purpose],sssPurposeBSS,.90:
 .10: MOV EAX,'3206'
      JSt EDX,sssPurposeCODE,.20:
      MOV EAX,'3205'
 .20: Msg EAX,EBX ; '3205 Emitting data to section [!1S] with PURPOSE=BSS.
      STC         ; '3206 Emitting code to section [!1S] with PURPOSE=BSS.
.90:EndProcedure SssCheckPurpose
↑ SssCreate@LT DataType, Stm
SssCreate@LT will create or reuse data literal section in current program.
The section name is one of @LT1 @LT2 @LT4 @LT8 @LT16 @LT32 @LT64 and it belongs to the last data segment declared in current program which has the PURPOSE=DATA+LITERALS set. If none such segment is found, the last segment with PURPOSE=DATA is selected.
If none data segment exists, an implicit one @LT will be created first.
Input
DataType LSB contains uppercase character with type of literal data: 'B','U','W','D','Q','T','O','Y','Z'.
Stm Pointer to the statement which creates the literal symbol.
Output
CF=0, EAX= ^SSS section of required type.
Error
CF=1, EAX=0 Errors are reported with macro Msg.
See also
SssCreate@RT
Invokes
ExpWidthOfDataType SssCreate SssFind
Invoked by
SymCreateLiteral
SssCreate@LT Procedure DataType, Stm
SssName LocalVar Size=8 ; Room for string "@LT"+decimal number.
     SUB ECX,ECX      ; Initialization.
     LEA EDI,[%SssName] 
     MOV EAX,"@LTx"
     STOSD
     XOR EAX,EAX
     STOSD
     MOV [%ReturnEAX],EAX
     Invoke ExpWidthOfDataType::,[%DataType] ; Decide section alignment and name.
     CMP CL,10 ; Aligment requested by the datatype.
     JNE .10:
     MOV CL,8 ; Alignment of TBYTE is 8.
.10: OR EAX,ECX
     JNZ .15:
     Msg '6676',[%DataType] ; Wrong literal type "!1Z".
     STC
     JMP .90:
.15: LEA ESI,[%SssName]
     MOV EAX,ECX
     MOV EBX,ECX ; Section alignment (1,2,4,8,16,32,64).
     LEA EDI,[ESI+3] ; Construct literal section name.
     StoD EDI
     SUB EDI,ESI
     MOV ECX,EDI ; Section name size.
     Invoke SssFind,sssSection,0,ESI,ECX,0 ; Try to find section by name.
     MOV [%ReturnEAX],EAX
     JC .20: ; If [@LTx] section does not exist yet.
     MOV ECX,[EAX+SSS.SegmPtr]
     SetSt [EAX+SSS.Status],sssUsed
     SetSt [EAX+SSS.Purpose],sssPurposeLITERAL
     JECXZ .18:
     SetSt [ECX+SSS.Status],sssUsed 
 .18:JMP .90: ; Segment or section @LTx was found.
 .20: ; New literal section will be created in data segment. 
      ; ESI,ECX=Name, EBX=alignment (1,2,4,8,16,32,64).
     MOV EDI,[%Stm]
     MOV EDX,[EDI+STM.Program]
     ; Find the last segment with data purpose where the new literal section will belong to.
     ; First prefer segment with purpose LITERAL, if any.
     ListGetLast [EDX+PGM.SssList]
     JZ .70:
 .30:JNSt [EAX+SSS.Status],sssSegment,.40:
     JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.40:
     JSt  [EAX+SSS.Purpose],sssPurposeDATA,.80: ; If data segment found.
 .40:ListGetPrev EAX
     JNZ .30:
     ListGetLast [EDX+PGM.SssList] ; In 2nd pass accept any data segment (without literals preference).
 .50:JNSt [EAX+SSS.Status],sssSegment,.60:
     JSt [EAX+SSS.Purpose],sssPurposeDATA,.80: ; If data segment found.
 .60:ListGetPrev EAX
     JNZ .50:
 .70: ; No data segment found, create one. Its name will be "@LT".
     LEA EAX,[EDX+PGM.Pgmopt]
     LEA EDI,[%SssName]
     MOV EAX,[EAX+PGMOPT.Status]
     AND EAX,pgmoptWidthMask ; Section width will be taken from program width.
     OR EAX,sssSegment+sssPrivate+sssNotBSS+sssImplicit+sssUsed
     Invoke SssCreate,[%Stm],0,EDI,3,EAX,sssPurposeDATA+sssPurposeLITERAL,64
 .80:MOV EDI,EAX ; Data segment where our literal section will belong to.
     SetSt [EDI+SSS.Status],sssUsed
     SetSt [EDI+SSS.Purpose],sssPurposeLITERAL ; Next time prefer this very segment for more literals.
     ; New literal section will be created in data segment EDI. 
     ; ESI,ECX=Name, EDX=PGM, EBX=alignment (1,2,4,8,16,32,64).
     MOV EAX,[EDI+SSS.Status]
     AND EAX,sssCombineMask|sssWidthMask ; Properties inherited from segment EDI.
     OR EAX,sssSection+sssNotBSS+sssImplicit+sssUsed
     PUSH EAX ; New section status.
       Invoke SssCreate,[%Stm],EDI,ESI,ECX,EAX,sssPurposeDATA+sssPurposeLITERAL,EBX
       MOV ESI,EAX ; ^SSS - just created section.
     POP EAX ; CreateSection may have incorrectly set .Status, .Puprpose and .SegmPtr.
     MOV [ESI+SSS.Status],EAX
     SetSt [ESI+SSS.Purpose],sssPurposeDATA+sssPurposeLITERAL
     MOV [ESI+SSS.SegmPtr],EDI
     MOV [%ReturnEAX],ESI
.90:EndProcedure SssCreate@LT
↑ SssCreate@RT PreviousSect, Stm
SssCreate@RT will create or reuse runtime section in current program.
The section name is [@RT0], [@RT1], [@RT2] etc and it belongs to the last code segment declared in current program which has PUPRPOSE=CODE+LITERALS set, or if none such exists, to the last code segment.
Runtime section [@RT0] hosts code literals defined with pseudoinstruction DI, e.g. LEA ESI,[=8*I"STOSD"], or with INSTR literal, e.g. CALL =I"RET".
Runtime section [@RT1] hosts PROC1/ENDPROC1 blocks and =I"machine instruction" code-literal data.
Only when the current section already is runtime literal too, a new one will be created with incremented decimal number in its name: [@RT2], [@RT3] etc.
Runtime sections have fixed alignment BYTE.
If no code segment exists, and implicit one @RT will be created first.
Input
PreviousSect is pointer to an existing current section (SSS) or 0. Section's .Name and .Purpose will be inspected to choose the name of created/reused @RTx section. When PreviousSect is NULL, procedure returns pointer to section [@RT0].
Stm is pointer to the statement which requires the runtime section, usually PROC1 or instruction referring =INSTR literal.
Output
CF=0, EAX= pointer to just created or reused section.
Error
CF=1 EAX=0 Errors are reported with macro Msg.
See also
SssCreate@LT.
Invokes
SssCreate SssFind
Invoked by
PseudoPROC1 SymCreateLiteral
SssCreate@RT Procedure PreviousSect, Stm
SssName LocalVar Size=8 ; Room for string "@RT"+decimal number.
      LEA EDI,[%SssName] ; Initialization.
      MOVD [EDI],"@RT0"
      MOV EDX,4
      MOV EBX,[%PreviousSect]
      TEST EBX
      JZ .10:
      MOVD [EDI],"@RT1"
      JNSt [EBX+SSS.Purpose],sssPurposeLITERAL,.10: ; If we weren't in literal section already.
      MOV ECX,[EBX+SSS.NameSize]
      MOV ESI,[EBX+SSS.NamePtr]
      CMP ECX,4
      JB .10: ; Name too short, it cannot be [@RTx].
      Compare ESI,3,EDI
      JNE .10: ; If does not start with "@RT".
      ; Current section already is runtime, select the next one.
      SUB ECX,3
      ADD ESI,3 ; Skip "@RT" in section name.
      LodD ESI,Size=ECX
      INC EAX  
      MOV EDX,EDI ; Temporary save pointer to new @RT name.
      ADD EDI,3
      StoD EDI,Size=5,Align=left ; Overwrite @RT number.
      SUB EDI,EDX
      XCHG EDI,EDX
 .10: ; EDI,EDX is now required section name, in most cases "@RT0" or "@RT1".
      Invoke SssFind,sssSection,0,EDI,EDX,0 ; Try to find section by name.
      MOV [%ReturnEAX],EAX
      JC .30: ; If [@RTx] section does not exist yet.
      MOV ECX,[EAX+SSS.SegmPtr]
      SetSt [EAX+SSS.Status],sssUsed
      SetSt [EAX+SSS.Purpose],sssPurposeLITERAL
      JECXZ .20:
      SetSt [ECX+SSS.Status],sssUsed
 .20: JMP .90: ; Section @RTx was found, it will be reused as is.
 .30: ; New code-literal section will be created in code segment. EDI,EDX=section name. 
      MOV EBX,[%Stm]
      MOV ECX,[EBX+STM.Program]
     ; Find the last segment with code purpose where the new runtime section will belong to.
     ; First prefer segment with purpose LITERALS, if any.
      ListGetLast [ECX+PGM.SssList]
      JZ .70:
 .40: JNSt [EAX+SSS.Status],sssSegment,.45:
      JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.45: ; First prefer segment with PURPOSE=LITERALS.
      JSt [EAX+SSS.Purpose],sssPurposeCODE,.80: ; If code segment found.
 .45: ListGetPrev EAX
      JNZ .40:
 .50: ListGetLast [ECX+PGM.SssList] ; In 2nd pass accept any code segment without sssPurposeLITERAL preference.
 .55: JNSt [EAX+SSS.Status],sssSegment,.60:
      JSt [EAX+SSS.Purpose],sssPurposeCODE,.80: ; If code segment found.
 .60: ListGetPrev EAX
      JNZ .55:
 .70: ; No code segment found, create one. Its name will be "@RT".
      MOV EAX,[ECX+PGM.Pgmopt+PGMOPT.Status]
      AND EAX,pgmoptWidthMask ; Segment width will be taken from program width.
      OR EAX,sssSegment+sssPrivate+sssNotBSS+sssImplicit+sssUsed
      Invoke SssCreate,[%Stm],0,EDI,3,EAX,sssPurposeCODE+sssPurposeLITERAL,1
 .80: MOV EBX,EAX ; Code segment where our runtime section will belong to.
      SetSt [EBX+SSS.Status],sssUsed
      SetSt [EBX+SSS.Purpose],sssPurposeLITERAL ; Next time prefer this very segment for more literals.
      ; New runtime section will be created in data segment EBX. EDI,EDX=Name, ECX=PGM.
      SetSt [EBX+SSS.Status],sssUsed
      MOV EAX,[EBX+SSS.Status]
      AND EAX,sssCombineMask|sssWidthMask ; Properties inherited from segment.
      OR EAX,sssSection+sssNotBSS+sssImplicit+sssUsed
      PUSH EAX ; New section status.
        Invoke SssCreate,[%Stm],EBX,EDI,EDX,EAX,sssPurposeCODE+sssPurposeLITERAL,1
        MOV ESI,EAX ; ^SSS just created.
      POP EAX ; CreateSection may have incorrectly set .Status, Purpose and .SegmPtr.
      MOV [ESI+SSS.Status],EAX
      SetSt [ESI+SSS.Purpose],sssPurposeCODE+sssPurposeLITERAL
      MOV [ESI+SSS.SegmPtr],EBX
      MOV [%ReturnEAX],ESI
 .90:EndProcedure SssCreate@RT
↑ SssPurposeToText sssPurpose, Buffer
SssPurposeToText converts segment purpose flags to their names.
Input
sssPurpose is a combination of SSS.Purpose flags in SssPurposeEncoding which specify segment purpose.
Buffer Pointer to BUFFER for the output text, reserved by caller.
Output
Output buffer is filled with purpose names, separated with + and zero-terminated.
Error
-
Example
Invoke SssPurposeToText, [EBX+SSS.Purpose],[%Buffer] ; Buffer now contains zero-terminated string such as CODE+DATA+LITERALS.
Invoked by
PgmListMap PseudopcDISPLAY
SssPurposeToText Procedure sssPurpose, Buffer
     SUB ECX,ECX  ; ECX specifies whether to use separator +.
     MOV EBX,[%Buffer]
     MOV EDX,[%sssPurpose]
PURPOSE %FOR %SssPurposeList
     JNSt EDX,sssPurpose%PURPOSE,.Not%PURPOSE:
     BufferStore EBX,=B'+',ECX ; ECX is either 0 or 1.
     MOV EDI,Dict_Purpose%PURPOSE::
     BufferStore EBX,[EDI+0],[EDI+4]
     MOV CL,1
.Not%PURPOSE:
     %ENDFOR PURPOSE
     BufferStoreByte EBX,0 ; Terminating zero.
    EndProcedure SssPurposeToText
↑ SssCreateExtern SymbolPtr, ProgramPtr
If the input symbol is external or imported and has not SYM.Section set, or if it is set to non-external SSS object, SssCreateExtern will create a new extern pseudosegment on SssList of its program and update Symbol.Section with this pseudosegment.
Input
SymbolPtr is pointer to external|imported SYM .
ProgramPtr is pointer to PGM where the external pseudosegment will be created.
Output
Symbol is assigned with an existing or new extern pseudosegment with identical name.
Error
-
Invokes
SssFind
Invoked by
PassDestroy PfDrectveDestroy PfcoffLoadModule PfomfLoadModule PgmCombine PgmCreateImportModule SymCreate SymDynamicLink
SssCreateExtern Procedure SymbolPtr, ProgramPtr
    MOV EBX,[%SymbolPtr]
    JNSt [EBX+SYM.Status],symExtern|symImport,.90:
    MOV EDX,[%ProgramPtr]
    Invoke SssFind,sssExtern,0,[EBX+SYM.NamePtr],[EBX+SYM.NameSize],EDX
    JNC .50:
    ; A new extern pseudosection will be created.
    ListNew [EDX+PGM.SssList],Zeroed=yes
    JC .90:
.50:MOV EDI,EAX ; ^SSS.
    SetSt [EDI+SSS.Status],sssExtern+sssCommon
    JNSt [EBX+SYM.Status],symImport,.60:
    SetSt [EDI+SSS.Status],sssImport
.60:MOVB [EBX+SYM.Status],'A'
    MOV [EBX+SYM.Section],EDI
    MOV [EDI+SSS.SegmPtr],EDI ; Extern pseudosegment is its own segment.
    MOV [EDI+SSS.SymPtr],EBX  ; Reference to the external/imported symbol from its pseudosegment.
    MOV ESI,[EBX+SYM.NamePtr]
    MOV ECX,[EBX+SYM.NameSize]
    MOV EDX,[EBX+SYM.LinePtr]
    MOV [EDI+SSS.NamePtr],ESI
    MOV [EDI+SSS.NameSize],ECX
    CMPD [EDI+SSS.LinePtr],0
    JNZ .90:
    MOV [EDI+SSS.LinePtr],EDX
    SUB ECX,ECX
    RstSt [EBX+SYM.Status],symEstimated
    MOV [EBX+SYM.OffsetLow],ECX
    MOV [EBX+SYM.OffsetHigh],ECX
.90:EndProcedure SssCreateExtern
↑ SssGetCoffCharacteristics Segment
Procedure SssGetCoffCharacteristics returns flags describing characteristics of the COFF section header as they are specified in PFCOFF_SECTION_HEADER encoding.
Input
Segment is pointer to SSS which is just compiled.
Output
EAX= set of PFCOFF_SECTION_HEADER.Characteristics flags depending on segment purpose and status.
Error
-
Invoked by
PfcoffSegmRawData
SssGetCoffCharacteristics Procedure Segment
     MOV EBX,[%Segment]
     MOV ESI,[EBX+SSS.Alignment]
     BSF EDX,ESI ; EDX is now 0=B, 1=W, 2=D, 3=Q, 4=O, .. 13=8192.
     CMP DL,13
     JBE .80:
     MOV DL,4
 .80:INC EDX
     SHL EDX,20 ; Conversion to SectionHeader.Characteristics alignment.
     MOV ESI,[EBX+SSS.Purpose]
PURPOSE %FOR %SssPurposeList
     JNSt ESI,sssPurpose%PURPOSE, .Not%PURPOSE
     PUSHD .Not%PURPOSE: ; Prepare return address from subprocedure .%PURPOSE.
     JMP .%PURPOSE:
.Not%PURPOSE:
     %ENDFOR PURPOSE
     JMP .90:
.CODE:
  OR EDX,pfcoffSCN_CNT_CODE+pfcoffSCN_MEM_EXECUTE+pfcoffSCN_MEM_READ
  RET
.DATA:
  OR EDX,pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_WRITE+pfcoffSCN_MEM_READ
  RET
.BSS:
.STACK:
  OR EDX,pfcoffSCN_CNT_UNINITIALIZED_DATA+pfcoffSCN_MEM_WRITE+pfcoffSCN_MEM_READ
  RET
.DEBUG:
.BASERELOC:
  OR EDX,pfcoffSCN_MEM_DISCARDABLE+pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_SHARED+pfcoffSCN_MEM_READ
  RET
.LITERAL:
.RESERVED:
  RET
.DRECTVE:
  OR EDX,pfcoffSCN_MEM_DISCARDABLE+pfcoffSCN_LNK_INFO+pfcoffSCN_LNK_REMOVE
  RET
.IMPORT:
  OR EDX,pfcoffSCN_CNT_CODE+pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_SHARED+pfcoffSCN_MEM_EXECUTE+pfcoffSCN_MEM_READ
  RET
.IAT:
  OR EDX,pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_SHARED+pfcoffSCN_MEM_READ
  RET
.GLOBALPTR:
  OR EDX,pfcoffSCN_GPREL
  RET
.RESOURCE:
  OR EDX,pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_READ
  RET
.EXPORT:
.EXCEPTION:
.SECURITY:
.COPYRIGHT:

.TLS:
.LOAD_CONFIG:
.BOUND_IMPORT:
.DELAY_IMPORT:
.CLR:
  OR EDX,pfcoffSCN_MEM_READ
  RET
.90:MOV [%ReturnEAX],EDX
   EndProcedure SssGetCoffCharacteristics
↑ SssLinkSegment Segm, Program
SssLinkSegment links sections of one specified segment Segm, i.e. it updates their bottom, top, symbols, relocations. Ordering of sections in linked segment:
  1. Base section (name identical with its segment),
  2. sections which are not literal, in the order as they were declared,
  3. data literal sections in alignment-descending order @LT64,@LT32,@LT16,@LT8,@LT4,@LT2,@LT1,
  4. code literal sections in name-ascending order @RT0,@RT1,@RT2,@RT3....
Segment bottom remains 0 after SssLinkSegment, sections bottoms are updated.
If Program.Status:pgmLinking is set, which happens at link time, sections are linked not only virtually, but their contents in .EmitBuffer and .RelocBuffer is appended to the base segment, and linked section is discarded.
Input
Segm is pointer to SSS base segment to which are its sections linked.
Program is pointer to PGM where the segment belongs.
Output
Symbol offsets are adjusted.
Invokes
SssFind SssLinkSection
Invoked by
PgmLinkSections
SssLinkSegment Procedure Segm, Program
OrgLow  LocalVar ; Segment origin.
OrgHigh LocalVar
LitVal  LocalVar ; Ordinal Nr of literal section 1,2,3..  or 64,32,16,8,4,2,1.
LitName LocalVar Size=16 ; Room for section name "@RT1","@RT2","@RT3".. or "@LT64","@LT32".."@LT1".
     MOV EBX,[%Program]
     MOV EDI,[%Segm]
     ; Step 1: link the base section which is identical with the segment EDI itself.
     SUB EAX,EAX ; Origin starts at VA=0.
     SUB EDX,EDX
     Invoke SssLinkSection, EDI,EDI,EBX,EAX,EDX
     MOV [%OrgLow],EAX
     MOV [%OrgHigh],EDX
     ; Step 2: link nonbase nonliteral sections of segment EDI.
     ListGetFirst [EBX+PGM.SssList]
.10: MOV ESI,EAX
     JNSt [ESI+SSS.Status],sssSection,.20: ; If ESI is not a section.
     CMP ESI,EDI
     JE .20: ; Skip the base section which is already linked.
     CMP [ESI+SSS.SegmPtr],EDI
     JNE .20: ; Skip when the section ESI does not belong to segment EDI.
     JSt [ESI+SSS.Purpose],sssPurposeLITERAL,.20: ; Skip literal sections.
     Invoke SssLinkSection,ESI,EDI,EBX,[%OrgLow],[%OrgHigh]
     MOV [%OrgLow],EAX
     MOV [%OrgHigh],EDX
.20: ListGetNext ESI ; Find the next SSS object to EAX.
     JNZ .10:
     JNSt [EDI+SSS.Purpose],sssPurposeDATA,.50:
     ; Step 3: link the data literal sections which belong to segment EDI.
     MOVD [%LitVal],64 ; Start with @LT section which has the strongest alignment.
     MOVD [%LitName],'@LT*'
.30: LEA ESI,[%LitName]
     MOV EAX,[%LitVal]
     LEA EDI,[ESI+3] ; Skip characters '@LT'.
     StoD EDI ; [%LitVal] now contains '@LT64','@LT32','@LT16', '@LT8', '@LT4' etc.
     SUB EDI,ESI ; Number of characters in litereal section name.
     Invoke SssFind,sssSection,0,ESI,EDI,0
     JC .40: ; If no such section was present.
     JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.40: ; If section [@LT*] was not literal (weird).
     MOV EDI,[%Segm]
     CMP [EAX+SSS.SegmPtr],EDI
     JNE .40: ; If the literal section doesn't belong to our segment.
     CMP EAX,EDI
     JE .40: ; If this is already linked base section.
     Invoke SssLinkSection, EAX,EDI,EBX,[%OrgLow],[%OrgHigh]
     MOV [%OrgLow],EAX  ; Save origin where the next section will start at.
     MOV [%OrgHigh],EDX
.40: SARD [%LitVal],1 ; Prepare for the next data literal section.
     JNC .30: ; CF=1 if [@LT1] has just been linked.
.50: MOV EDI,[%Segm]
     JNSt [EDI+SSS.Purpose],sssPurposeCODE,.90:
     ; Step 4: link the code literal sections which belong to segment EDI.
     MOVD [%LitVal],0 ; Start with [@RT0].
     MOVD [%LitName],'@RT0'
.60: LEA ESI,[%LitName]
     MOV EAX,[%LitVal]
     LEA EDI,[ESI+3] ; Skip characters '@RT'.
     StoD EDI
     SUB EDI,ESI
     Invoke SssFind, sssSection,0,ESI,EDI,0
     JNC .70:
     CMPD [%LitVal],0
     JE .80:
     JMP .90: ; If none section above @RT0 exists, neither the following one can exist. All sections are linked.
.70: JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.80: ; If section [@RT*] was not literal (weird).
     MOV EDI,[%Segm]
     CMP [EAX+SSS.SegmPtr],EDI
     JNE .80: ; If runtime section doesn't belong to our segment.
     CMP EAX,EDI
     JE .80: ; If this is already linked base section.
     Invoke SssLinkSection,EAX,EDI,EBX,[%OrgLow],[%OrgHigh]
     MOV [%OrgLow],EAX
     MOV [%OrgHigh],EDX
.80: INCD [%LitVal] ; Prepare for the next code literal section.
     JMP .60:
.90: ; Update segment virtual size if emitted size is lower.
     JNSt [EBX+PGM.Status],pgmLinking,.99:
     MOV EDI,[%Segm]
     JNSt [EDI+SSS.Status],sssNotBSS,.99:
     BufferRetrieve [EDI+SSS.EmitBuffer]
     MOV [EDI+SSS.TopLow],ECX
.99: EndProcedure SssLinkSegment
SssLinkSection Procedure Sect, Segment, PgmPtr, OrigLow, OrigHigh
    MOV ESI,[%Sect]
    MOV EDI,[%Segment]
    MOV EBX,[%PgmPtr]
    CMP ESI,EDI                                 ; Test if it is the base section (identical with segment),
    JE .90:                                     ; Base section doesn't need relocation.
    MOV EAX,[%OrigLow]
    MOV EDX,[%OrigHigh]
    Invoke ExpAlign::,EAX,[ESI+SSS.Alignment],0 ; Align origin by section alignment.
    ADD EAX,ECX                                 ; ECX is size of alignment stuff.
    ADC EDX,0                                   ; EDX:EAX is now new aligned section's bottom.
    SUB EAX,[ESI+SSS.BottomLow]                 ; Subtract old section bottom.
    SBB EDX,[ESI+SSS.BottomHigh]                ; EDX:EAX is now delta (section relocation value).
    JNZ .10:
    TEST EAX
    JZ .30:                                     ; If delta=0, no section relocation is required.
.10:JSt [EBX+PGM.Status],pgmLastPass,.30:       ; If the final pass just ended.
    JNSt [EBX+PGM.Status],pgmFixingPass,.15:
    TEST EDX
    JS .30:                                     ; If the fixing pass just ended, only positive relocation is allowed.
.15:; Relocation of section ESI by delta EDX:EAX.
    ADD [ESI+SSS.BottomLow],EAX
    ADC [ESI+SSS.BottomHigh],EDX
    ADD [ESI+SSS.TopLow],EAX
    ADC [ESI+SSS.TopHigh],EDX
    MOV ECX,EAX
    ; Symbols which belong to the section ESI will be relocated by EDX:ECX.
    ListGetFirst [EBX+PGM.SymList]
    JZ .30:
.20:CMP [EAX+SYM.Section],ESI
    JNE .25:                                    ; Skip if the symbol EAX is not from section ESI.
    ADD [EAX+SYM.OffsetLow],ECX
    ADC [EAX+SYM.OffsetHigh],EDX
    RstSt [EAX+SYM.Status],symFixed
.25:ListGetNext EAX
    JNZ .20:
.30:JNSt [ESI+SSS.Status],sssNotBSS,.35:
    SetSt [EDI+SSS.Status],sssNotBSS            ; If section contains static data, segment does so as well.
.35:JNSt [EBX+PGM.Status],pgmLinking,.85:
    ; At link-time the contents of section ESI will be flushed to segment EDI.
    JNSt [EDI+SSS.Status],sssNotBSS,.60:        ; Skip if no initialized data to flush.
    ; Segment contains initialized data or code, contents .EmitBuffer is valid.
    ; Fill the intersection gap.
    MOV EAX,[ESI+SSS.BottomLow]                 ; Aligned and updated section's bottom.
    MOV EDX,[ESI+SSS.BottomHigh]
    BufferRetrieve [EDI+SSS.EmitBuffer]         ; Segment's contents.
    MOV ESI,[%Sect]
    SUB EAX,ECX                                 ; Subtract emitted size of segment EDI.
    SBB EDX,0                                   ; EDX:EAX is now size of gap between contents. It may be negative.
    JNZ .40:
    TEST EAX
    JZ .55:                                     ; If no alignment stuff necessary.
.40:TEST EDX
    JS .50:
    MOV ECX,EAX                                 ; ECX bytes of segment contents will be filled with intersection stuff.
    XOR EAX,EAX
    JNSt [EDI+SSS.Purpose],sssPurposeCODE,.45:
    JSt [EDI+SSS.Purpose],sssPurposeDATA|sssPurposeBSS|sssPurposeSTACK|sssPurposeOptionalMask,.45:
    MOV AL,0x90                                 ; Intersection stuff is NOP only when the segment is pure CODE.
.45:BufferStoreByte [EDI+SSS.EmitBuffer],EAX
    LOOP .45:
    JMP .55:
.50:NEG EAX
    BufferDecrement [EDI+SSS.EmitBuffer],Size=EAX
.55:; Copy section emitted contents to the segment EDI.
    BufferRetrieve [ESI+SSS.EmitBuffer]
    BufferStore [EDI+SSS.EmitBuffer],ESI,ECX 
.60: ; Copy section relocations to the segment EDI.
    MOV ESI,[%Sect]
    BufferRetrieve [ESI+SSS.RelocBuffer]
    JECXZ .80:
    MOV EDX,SIZE# RELOC
.65:MOV EAX,[ESI+RELOC.Target] ; EAX is now section which the relocation refers to.
    TEST EAX                   ; RELOC.Target may be 0, which is treated as segment at absolute address 0.
    JZ .75:
    MOV EAX,[EAX+SSS.SegmPtr]
    MOV [ESI+RELOC.Target],EAX ; Replace relocation's section with relocation's segment.
.75:BufferStore [EDI+SSS.RelocBuffer],ESI,EDX
    ADD ESI,EDX
    SUB ECX,EDX
    JA .65:                    ; The next RELOC object.
.80:MOV ESI,[%Sect]
    MOV EAX,[ESI+SSS.TopLow]
    MOV EDX,[ESI+SSS.TopHigh]
    MOV [EDI+SSS.TopLow],EAX
    MOV [EDI+SSS.TopHigh],EDX
    ListRemove [EBX+PGM.SssList],ESI ; Discard the linked section ESI.
.85:MOV EAX,[ESI+SSS.BottomLow] ; Althoug the section might have been discarded, its data are still available.
    MOV EDX,[ESI+SSS.BottomHigh]
    MOV [ESI+SSS.OrgLow],EAX    ; Reset VA of the section for the next pass.
    MOV [ESI+SSS.OrgHigh],EDX
.90:JNSt [EBX+PGM.Status],pgmLinking,.95:
    RstSt [EDI+SSS.Status],sssSection ; The base segment is not a section any longer.
.95:MOV EAX,[EDI+SSS.BottomLow]
    MOV EDX,[EDI+SSS.BottomHigh]
    MOV [EDI+SSS.OrgLow],EAX     ; Reset VA of the segment for the next pass.
    MOV [EDI+SSS.OrgHigh],EDX
    MOV EAX,[ESI+SSS.TopLow]
    MOV EDX,[ESI+SSS.TopHigh]
    MOV [%ReturnEAX],EAX
    MOV [%ReturnEDX],EDX
   EndProcedure SssLinkSection
↑ SssResizeGroup Group, Program
SssResizeGroup recalculates virtual size of the Group, considering all segments of Program which belong to the group.
Group.Bottom will be the lowest bottom and Group.Top the highest top of all its segments.
Input
Group is pointer to the SSS group.
Program is pointer to PGM where the Group belongs.
Output
Group.Bottom and Group.Top are recalculated.
Error
Errors are reported with macro Msg.
Invoked by
PfOutput PgmCombine PgmLinkImage
SssResizeGroup Procedure Group, Program
    MOV EBX,[%Program]
    MOV EDI,[%Group]
    XOR EAX,EAX ; Initialize group bounderies to temporary values .Bottom=-1, .Top=0.
    MOV [EDI+SSS.TopLow],EAX
    MOV [EDI+SSS.TopHigh],EAX
    NOT EAX
    MOV [EDI+SSS.BottomLow],EAX
    MOV [EDI+SSS.BottomHigh],EAX
    ListGetFirst [EBX+PGM.SssList] ; Walk through segments.
.30:MOV ECX,EAX
    JNSt [ECX+SSS.Status],sssSegment,.70:
    MOV EAX,[ECX+SSS.GroupPtr]
    CMP EDI,EAX
    JE .36:
    TEST EAX
    JZ .70:
    MOV EAX,[EAX+SSS.GroupPtr] ; EAX might be a group from linked module.
    CMP EDI,EAX
    JNE .70:
.36:; ECX is a segment of group EDI. Adjust group's .Bottom and .Top.
    MOV EDX,[ECX+SSS.BottomHigh]
    MOV EAX,[ECX+SSS.BottomLow]
    CMP EDX,[EDI+SSS.BottomHigh]
    JA .50:
    JB .40:
    CMP EAX,[EDI+SSS.BottomLow]
    JAE .50:
.40:MOV [EDI+SSS.BottomLow],EAX
    MOV [EDI+SSS.BottomHigh],EDX ; Group bottom was updated.
.50:MOV EDX,[ECX+SSS.TopHigh]
    MOV EAX,[ECX+SSS.TopLow]
    CMP EDX,[EDI+SSS.TopHigh]
    JB .70:
    JA .60:
    CMP EAX,[EDI+SSS.TopLow]
    JBE .70:
.60:MOV [EDI+SSS.TopLow],EAX
    MOV [EDI+SSS.TopHigh],EDX ; Group top was updated.
.70:ListGetNext ECX ; The next segment.
    JNZ .30:
    XOR EAX,EAX ; If no segment of the group EDI was found, set group .Bottom and .Top back to 0.
    NOT EAX
    CMP EAX,[EDI+SSS.BottomLow]
    JNE .90:
    CMP EAX,[EDI+SSS.BottomHigh]
    JNE .90:
    XOR EAX,EAX ; If no segment of the group EDI was found, set group .Bottom and .Top back to 0.re>
    MOV [EDI+SSS.BottomLow],EAX
    MOV [EDI+SSS.BottomHigh],EAX
.90:EndProcedure SssResizeGroup
↑ SssCombine SssObject, BasePgm
SssCombine will combine one segment or group or external pseudosegment of linked program to the current base program (main). Combined SssObject no longer belongs to its previous program but its (elevated) .Bottom will be used to fixup symbols and relocations which may refer to it.
If the name of SSS object from linked program does not match any object in the base program, it is combined as PRIVATE, i.e. the object is added (copied) to the base program, including its emitted contents and relocations. Otherwise the object is combined according to rules in the table below.
In PUBLIC and COMMON combine methods are both objects united to one: either concatenated with respect to segments alignment (if PUBLIC), or merged to common origin 0 (if COMMON).
Combine rules for objects with matching names
SSS object types sssExternsssGroupsssSegment
+sssPublic
sssSegment
+sssStack
sssSegment
+sssCommon
sssSegment
+sssPrivate
sssExternCOMMONPRIVATEPRIVATEPRIVATEPRIVATEPRIVATE
sssGroupPRIVATEPUBLICPRIVATEPRIVATEPRIVATEPRIVATE
sssSegment+sssPublicPRIVATEPRIVATEPUBLICPUBLICPUBLICPRIVATE
sssSegment+sssStackPRIVATEPRIVATEPUBLICPUBLICPUBLICPRIVATE
sssSegment+sssCommonPRIVATEPRIVATEPUBLICPUBLICCOMMONPRIVATE
sssSegment+sssPrivatePRIVATEPRIVATEPRIVATEPRIVATEPRIVATEPRIVATE

SssCombine only affects members SSS.Bottom, SSS.Top, SSS.SegmPtr, SSS.GroupPtr of the input object. Emitted contents and relocations are copied from the input object to BasePgm but not fixed up yet. Relocation origins will be fixed up later, in RelocFixup invoked from PgmCombine.87:.
Linked SssObject's references (members .GroupPtr and .SegmPtr) are redirected to their new position on BasePgm.SssList.
Global symbols and relocations of linked program are not updated here.

Input
SssObject is pointer to SSS object (segment/group/extern) from the linked program.
BasePgm is pointer to PGM - program which is the SssObject linked to.
Output
SssObject is combined to the current program.
Invoked by
PgmCombine
Invokes
ExpAlign SssFind
Tested by
t7940 t7943
SssCombine Procedure SssObject, BasePgm
    MOV ESI,[%SssObject]
    MOV EBX,[%BasePgm]
    MOV EAX,[ESI+SSS.Status]
    AND EAX,sssSegment+sssExtern+sssGroup
    Dispatch EAX,sssSegment,sssExtern,sssGroup
    JMP .End: ; Ignore other SSS types (actually no section or structure can pass thru PgmLoad).
.sssGroup: ; Groups always combine as PUBLIC. They have no contents.
    Invoke SssFind,sssGroup,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX ; Search in BasePgm.
    JNC .G5: ; Do not copy linked group to base program if it already exists there.
    ListStore [EBX+PGM.SssList],ESI ; Copy group ESI specified in linked module.
.G5:MOV [EAX+SSS.GroupPtr],EAX ; Pointer to an existing group in the base program (to itself).
    MOV [ESI+SSS.GroupPtr],EAX ; Make abandoned linked group refer to the base group with the same name.
    JMP .End:
.sssExtern: ; Extern pseudosegments always combine as COMMON. They have no contents.
    ; If the pseudosegment belons to imported symbol, the combined pseudosegment will be flagged sssImport.
    Invoke SssFind,sssExtern,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX ; Search in BasePgm.
    JNC .E5: ;  Do not copy linked extern pseudosegment to base program, if it already exists there.
    ListStore [EBX+PGM.SssList],ESI ; Otherwise add the linked extern to BasePgm.
.E5:MOV [EAX+SSS.SegmPtr],EAX ; Pointer to an existing EXTERN in the base program (to itself).
    MOV [ESI+SSS.SegmPtr],EAX ; Make abandoned linked extern refer to the base extern with the same name.
    JNSt [ESI+SSS.Status],sssImport,.End:
    SetSt [EAX+SSS.Status],sssImport 
    JMP .End:
.sssSegment: ; Segments combine according to their COMBINE= property.
    MOV EAX,sssCombineMask
    AND EAX,[ESI+SSS.Status]
    Dispatch EAX,sssPublic,sssCommon,sssStack
    ; Undispatched object uses combine method PRIVATE.
.PRIVATE: ; PRIVATE combining. ; Linked non-existing-yet or private segment ESI
    ; will be copied to base Pgm.SssList, although homonymous segment may already happily exist there.
    ListStore [EBX+PGM.SssList],ESI ; Copy old segment to base program including its contents.
    MOV [EAX+SSS.SegmPtr],EAX  ; Let the new just linked segment refer to itself.
    MOV [ESI+SSS.SegmPtr],EAX  ; Also let the abandoned linked segment refer to the new location in BasePgm.
    XOR EAX,EAX
    MOV [ESI+SSS.EmitBuffer],EAX  ; Invalidate contents of old linked segment.
    MOV [ESI+SSS.RelocBuffer],EAX
    JMP .End:
.sssCommon: ; Combine method COMMON.
    Invoke SssFind,sssSegment,sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
    JNC .COMMON: ; Only if both segments have COMBINE=COMMON.
    Invoke SssFind,sssSegment,sssPublic|sssStack,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
    JNC .PUBLIC: ; Combine COMMON segment with PUBLIC|STACK segment as PUBLIC.
    JMP .PRIVATE:; Combine COMMON segment with PRIVATE segment as PRIVATE.
.sssStack: ; Combine method STACK.
    Invoke SssFind,sssSegment,sssStack,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
    JNC .PUBLIC: ; Segments with stack-combine method link as public (they are concatenated).
    Invoke SssFind,sssSegment,sssPublic|sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
    JNC .PUBLIC: ; Combine STACK segment with PUBLIC|COMMON segment as PUBLIC.
    JMP .PRIVATE:; Combine STACK segment with PRIVATE segment as PRIVATE.
.sssPublic: ; Combine method PUBLIC.
    Invoke SssFind,sssSegment,sssPublic,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
    JNC .PUBLIC: ; Combine PUBLIC segment with PUBLIC segment as PUBLIC.
    Invoke SssFind,sssSegment,sssStack|sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
    JNC .PUBLIC: ; Combine PUBLIC segment with STACK|COMMON segment as PUBLIC.
    JMP .PRIVATE:; Combine PUBLIC segment with PRIVATE segment as PRIVATE.
.COMMON: ; COMMON combining. Both linked (ESI) and base (EAX) segments remain on bottom=0.
    MOV EDI,EAX ; Base segment to which is the segment ESI combined.
    MOV [ESI+SSS.SegmPtr],EDI ; Let abandoned linked segment refer to base segment.
    MOV EDX,[ESI+SSS.TopHigh] ; New base segment virtual size will be set to the greater of both.
    MOV EAX,[ESI+SSS.TopLow]
    CMP EDX,[EDI+SSS.TopHigh]
    JB .C3:
    JA .C2:
    CMP EAX,[EDI+SSS.TopLow]
    JBE .C3:
.C2:MOV [EDI+SSS.TopLow],EAX
    MOV [EDI+SSS.TopHigh],EDX
.C3:JMP .End:
.PUBLIC: ; PUBLIC combining. Linked segment ESI will be aligned and appended to base segment EAX.
    MOV EDI,EAX
    MOV [ESI+SSS.SegmPtr],EDI ; Let the linked segment refer to the existing base segment EDI.
    ; Merge properties of public segment combination.
    MOV EAX,[ESI+SSS.Purpose] ; Linked purpose.
    OR  [EDI+SSS.Purpose],EAX
    MOV EAX,sssNotBSS
    AND EAX,[ESI+SSS.Status]
    OR  [EDI+SSS.Status],EAX
    MOV EAX,[ESI+SSS.Alignment]
    CMP EAX,[EDI+SSS.Alignment]
    JNA .P2:
    MOV [EDI+SSS.Alignment],EAX ; Use greater alignment from both segments, if they are different.
.P2:JSt [ESI+SSS.Purpose],sssPurposeDRECTVE|sssPurposeOptionalMask,.P4:
    ; Width of segments with standard purpose CODE|DATA|BSS|STACK must match.
    MOV EAX,sssWidthMask
    MOV EDX,EAX
    AND EAX,[ESI+SSS.Status]
    JZ .P4: ; Segment from the linked segment may have unspecified width, this is OK.
    AND EDX,[EDI+SSS.Status]
    CMP EAX,EDX ; Check segment width match.
    JE .P4:
    Msg '7718',ESI ; Cannot link segments [!1S] which have different width.
    JMP .End:
.P4:MOV EAX,[EDI+SSS.TopLow]
    MOV EDX,[EDI+SSS.TopHigh] ; EDX:EAX is virtual base segment size.
    Invoke ExpAlign::,EAX,[ESI+SSS.Alignment],0 ; Align base top according to linked-segment alignment.
    ADD EAX,ECX ; Add alignment stuff size to the base top.
    ADC EDX,0 ; EDX:EAX is now aligned bottom of linked segment ESI, i.e. delta for future fixup.
    ADD [ESI+SSS.BottomLow],EAX
    ADC [ESI+SSS.BottomHigh],EDX
    ADD [ESI+SSS.TopLow],EAX
    ADC [ESI+SSS.TopHigh],EDX
    Msg cc=NZ,'8525',ESI ; Size of segment [!1S] exceeded 4 GB.
    MOV EAX,[ESI+SSS.TopLow]
    MOV EDX,[ESI+SSS.TopHigh]
    MOV [EDI+SSS.TopLow],EAX  ; Update the new top of base segment.
    MOV [EDI+SSS.TopHigh],EDX
    MOV EBX,ESI
    ; Copy the raw contents of combined segment EBX to base segment EDI.
    BufferRetrieve [EDI+SSS.EmitBuffer]
    SUB ECX,[EBX+SSS.BottomLow]
    JBE .P5:
    ; Emitted contents in base segment is above its virtual size. Truncate.
    BufferDecrement [EDI+SSS.EmitBuffer],Size=ECX
    JMP .P7:
.P5: ; Emitted contents in base segment is below virtual size. Supplement some stuff.
    NEG ECX
    JZ .P7: ; If emitted contents in base segment exactly matches its virtual size.
    XOR EDX,EDX ; Default alignment stuff is 0x00.
    JNSt [EBX+SSS.Purpose],sssPurposeCODE,.P6:
    JNSt [EDI+SSS.Purpose],sssPurposeCODE,.P6:
    JSt [EDI+SSS.Purpose],sssPurposeDATA|sssPurposeBSS|sssPurposeSTACK,.P6:
    MOV DL,0x90 ; Alignment stuff is 0x90 only when both combined segments have pure CODE purpose.
.P6:BufferStoreByte [EDI+SSS.EmitBuffer],EDX ; Base segment alignment padding.
    DEC ECX
    JNZ .P6:
.P7:BufferRetrieve [EBX+SSS.EmitBuffer]
    BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
    ; Copy relocations of combined segment EBX to base segment EDI.
    BufferRetrieve [EBX+SSS.RelocBuffer]
    BufferStore [EDI+SSS.RelocBuffer],ESI,ECX
.End:EndProcedure SssCombine
↑ SssEmitAlignment Section, HowManyBytes, OutBuffer
SssEmitAlignment will emit %HowManyBytes of alignment stuff to %OutBuffer. The stuff is 0x00 when %Section.Purpose:sssPurposeCODE is reset, otherwise it represents optimised operation code of multibyte NOP instruction, as specified in No-operation encoding table.
Input
Section is pointer to SSS whose origin is being aligned.
HowManyBytes is unsigned integer number.
OutBuffer is pointer to BUFFER where the stuff will be written.
Output
Contents of OutBuffer is appended with alignment stuff.
See also
No-operation encoding
Invoked by
PseudoALIGN SssEmit StmListing
Tested by
t2508
SssEmitAlignment Procedure Section, HowManyBytes, OutBuffer
    MOV ECX,[%HowManyBytes]
    MOV EDI,[%Section]
    TEST ECX
    JZ .90:
    MOV EBX,[%OutBuffer]
    JSt [EDI+SSS.Purpose],sssPurposeCODE,.60:
    ; Alignment stuff in noncode section is 0x00.
.10:BufferStoreByte EBX,0x00
    DEC ECX
    JNZ .10:
    JMP .90:

.Nop:PROC ; Emit EDX bytes of NOP1..NOP9 to buffer EBX. EDI=^SSS.
[.data]   ; Following table defines offsets of NOP opcode in string .N:
;NOPx      1    2    3    4    5    6    7    8    9
.16b086:DB 75, 76,  75,  76,  75,  76,  75,  76,  75
.16b686:DB 33, 32,  73,  28,  23,  67,  66,   9,  57
.32b386:DB 33, 32,  54,  50,  49,  43,  36,  35,  34
.32b686:DB 33, 32,  29,  24,  18,  17,  10,   1,   0
;64bX64 EQU 032b686
   ;   0    1    2    3    4    5    6    7    8    9
.N:DB 0x66,0x0F,0x1F,0x84,0x20,0x00,0x00,0x00,0x00,0x67,\
   \  10   11   12   13   14   15   16   17   18   19
      0x0F,0x1F,0x80,0x00,0x00,0x00,0x00,0x66,0x0F,0x1F,\
   \  20   21   22   23   24   25   26   27   28   29
      0x44,0x20,0x00,0x67,0x0F,0x1F,0x40,0x00,0x67,0x0F,\
   \  30   31   32   33   34   35   36   37   38   39
      0x1F,0x00,0x66,0x90,0x66,0x3E,0x8D,0x84,0x20,0x00,\
   \  40   41   42   43   44   45   46   47   48   49
      0x00,0x00,0x00,0x8D,0x80,0x00,0x00,0x00,0x00,0x3E,\
   \  50   51   52   53   54   55   56   57   58   59
      0x8D,0x44,0x20,0x00,0x8D,0x40,0x00,0x67,0x0F,0x1F,\
   \  60   61   62   63   64   65   66   67   68   69
      0x84,0x20,0x00,0x00,0x00,0x00,0x66,0x67,0x0F,0x1F,\
   \  70   71   72   73   74   75   76   77   78   79
      0x44,0x20,0x00,0x66,0x67,0x90,0x87,0xC9,0x87,0xD2,\
   \  80   81   82   83
      0x87,0xDB,0x87,0xE4
[.text]
    JNSt [Ea.Eaopt.Machine::],iiCPU_686, .20:
    ; Machine supports real NOP.
    MOV ESI, .32b686:
    JSt [EDI+SSS.Status],sssWidth32 | sssWidth64, .50:
    MOV ESI, .16b686:
    JMP .50:
.20:; Machine does not support real NOP.
    MOV ESI, .16b086:
    JSt [EDI+SSS.Status],sssWidth16, .50:
    MOV ESI, .32b386:
.50:MOV ECX,EDX ; Stuff size 1..9
    MOVZX EDX,[ESI+EDX-1],DATA=BYTE
    LEA ESI,[.N: + EDX] ; ESI points to real NOP opcode (ECX bytes long).
    BufferStore EBX,ESI,ECX
    Msg cc=C,'9314',SssEmitAlignment ;Allocation error storing to buffer in !1H.
    RET
    ENDP .Nop:

.60:; Alignment stuff in code section.
    MOV EDX,9
    JECXZ .90:
    SUB ECX,EDX
    JB  .80:
    PUSH ECX
      CALL .Nop:
    POP ECX
    JMP .60:
.80:ADD EDX,ECX
    CALL .Nop:
.90:EndProcedure SssEmitAlignment
↑ SssEmit Sss, EmitBuffer, RelocBuffer, Alignment
SssEmit stores alignment and data from Stm.EmitBuffer to Sss.EmitBuffer. Then it stores and patches relocations from Stm.RelocBuffer to Sss.RelocLBuffer.
It is invoked from StmFlush after each statement has been executed.
Input
Sss Pointer to section where data will be emitted.
EmitBuffer Pointer to BUFFER with code emitted by the current statement. It is empty in non-emitting statement. It may be NULL when there is nothing to emit.
RelocBuffer Pointer to BUFFER with RELOC records generated in the statement. It must be NULL when the program is not in the last pass.
Alignment Plain signed integer number of alignment stuff bytes which should add to Sss.Org before storing the data from %EmitBuffer to Stm.EmitBuffer. It is non-negative when the emitted data need alignment, either by explicit keyword ALIGN= in the current statement, or when AUTOALIGN is enabled.
It can be nonzero in ORG-type statements $ EQU expression. Alignment can also be negative in statements like $ EQU $-8.
Output
Section properties are updated.
Error
Errors are reported with macro Msg.
Invoked by
PassCreate StmFlush SymCreateLiteral
Invokes
RelocUniq SssEmitAlignment
SssEmit Procedure Sss, EmitBuffer, RelocBuffer, Alignment
      MOV EDI,[%Sss]
      MOV EAX,[%Alignment]
      CDQ
      BufferResize [EDI+SSS.EmitBuffer],EAX
      ADD EAX,[EDI+SSS.OrgLow]
      ADC EDX,[EDI+SSS.OrgHigh]      ; EDX:EAX is now the new aligned .Org.
      CMP EDX,[EDI+SSS.BottomHigh]
      JA .20:
      JB .E6555:
      CMP EAX,[EDI+SSS.BottomLow]
      JAE .20:
.E6555:Msg '6555'                    ; Offset out of section limits.
      MOV EAX,[EDI+SSS.BottomLow]    ; Negative %Alignment underflowed, let Sss.Org = Sss.Bottom.
      MOV EDX,[EDI+SSS.BottomHigh]
.20:  MOV [EDI+SSS.OrgLow],EAX       ; Sss.Org is now aligned as requested by %Alignment.
      MOV [EDI+SSS.OrgHigh],EDX
      SUB EAX,[EDI+SSS.TopLow]
      SBB EDX,[EDI+SSS.TopHigh]
      JB .30:
      ; Aligned .Org is EAX bytes above old .Top,
      ; the gap is virgin yet and it will be populated with alignment stuff.
      MOV EBX,EAX
      BufferDecrement [EDI+SSS.EmitBuffer],Size=EBX
      Invoke SssEmitAlignment, EDI,EBX,[EDI+SSS.EmitBuffer]
.30:  MOV EBX,[EDI+SSS.OrgLow]
      MOV EDX,[EDI+SSS.OrgHigh]
      BufferRetrieve [%EmitBuffer]
      JECXZ .50:
      ADD [EDI+SSS.OrgLow],ECX
      ADC [EDI+SSS.OrgHigh],0
      BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
      SetSt [EDI+SSS.Status],sssUsed
.50:  ; Now store relocations, if any.
      BufferRetrieve [%RelocBuffer]  ; If not the final pass, RelocBuffer was not specified.
      JECXZ .70:                     ; If %RelocBuffer is unspecified or empty.
      ; There are unpatched relocations in statement. ESI points to RELOC, EDX:EBX is aligned Org before emit.
 .60: ADD [ESI+RELOC.OrgLow],EBX
      ADC [ESI+RELOC.OrgHigh],EDX    ; Relocation position is now patched.
      MOV [ESI+RELOC.Section],EDI
      BufferStore [EDI+SSS.RelocBuffer],ESI,SIZE#RELOC
      ADD ESI,SIZE# RELOC
      SUB ECX,SIZE#RELOC
      JA .60:                        ; If there are more relocations in one statement.
      Invoke RelocUniq::,[EDI+SSS.RelocBuffer]
 .70: ; Update Sss.Top if it is below the new Sss.Org.
      MOV EAX,[EDI+SSS.OrgLow]
      MOV EDX,[EDI+SSS.OrgHigh]
      CMP EDX,[EDI+SSS.TopHigh]
      JB .90:
      JA .80:
      CMP EAX,[EDI+SSS.TopLow]
      JBE .90:
 .80: MOV [EDI+SSS.TopLow],EAX
      MOV [EDI+SSS.TopHigh],EDX
 .90:EndProcedure SssEmit
 ENDPROGRAM sss

▲Back to the top▲