| Index | Manual | Download | Source | Macros | Embedded word Case ins.
Sitemap | Links | Forum | Tests | Projects

# sss.htm
Class
SSS
Enumeration
SssPurposeList
Encodings
SssEnc
SssPurposeEnc
Procedures
SssCheckDirty
SssCheckPurpose
SssCombine
SssCreate@LT
SssCreate@RT
SssCreateExtern
SssCreateGroup
SssCreateSe
SssCreateStructure
SssCreateImplicit
SssEmit
SssEmitAlignment
SssFindByIndex
SssFindByName
SssFindByPurpose
SssGetCoffCharacteristics
SssGetSegm
SssGuessPurpose
SssLinkSections
SssOrderSections
SssPurposeToText
SssResizeGroup
SssRelocResolve
SssResolve
SssUpdateByEmit

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). 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` . See also Symbol and Segment binding.

Default implicit segments are created at PgmCreateProgram by SssCreateImplicit for each purpose (code, rodata, data, bss, stack), actual default segment names depend on the program format and model. Other SSS object may be created in the same program with explicit STRUC, GROUP, SEGMENT, [section], EXTERN statements, they can be used instead. Unused default segments will be abandoned when they are empty (nothing was emitted to them).

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).

        EUROASM NOWARN=2101
    sss PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
        INCLUDEHEAD euroasm.htm, \  Interface (structures, symbols and macros) of other modules used in this source.
                    dict.htm,ea.htm,eaopt.htm,exp.htm,ii.htm,msg.htm,pass.htm,pf.htm, \
                    pfcoff.htm,pgm.htm,pgmopt.htm,reloc.htm,stm.htm,sym.htm,var.htm

     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
  --- | --- | --- | ---
  sssStructure | 0 | 0 | 0
  sssSection | section VA | 0 or ^explicit group | ^segment
  sssSegment | 0 | 0 or ^explicit group | ^self
  sssGroup | 0 | ^self | 0 or ^base segment
  sssExtern | 0 | 0 | ^extern

  SSS pointers at link time
  Object type | .Bottom | .GroupPtr | .SegmPtr
  --- | --- | --- | ---
  sssStructure | 0 | 0 | 0
  sssSection | N/A | N/A | N/A
  sssSegment | segment VA | ^explicit or implicit group | ^self
  sssGroup | group VA | ^self | ^base segment
  sssExtern | extern VA | ^explicit or implicit group | ^extern

    SSS STRUC         ; +00h.
    .NamePtr      D D ; Object name without []. In case of sssExtern it is the symbol name.
    .NameSize     D D ; Size of object name.
    .ClassPtr     D D ; Unquoted identifier specified by SEGMENT CLASS='ClassName'. It may be empty.
    .ClassSize    D D ; Netto size of segment ClassName.
                      ; +10h.
    .BottomLow    D D ; Base offset of section in segment or segment's bottom address. 0 when unlinked yet and in sssStructure..
    .BottomHigh   D D ;     Bottom is 0 at assembly time for structures, segments, groups.
    .SegmPtr      D D ; ^SSS sssSegment where this section|segment|group belongs to. It may point to itself. Never 0.
    .GroupPtr     D D ; ^SSS sssGroup where this section|segment|group belongs to. It may point to itself, or 0 for nongrouped segments.
                      ; +20h.
    .Purpose      D D ; Purpose of the section or segment in SssPurposeEncoding.
    .SymPtr       D D ; ^SYM associated with this sssSegment|sssSection|sssExtern. 0 in sssStructure.
    .LinePtr      D D ; Pointer to the physical line in mapped source which declared the object.
    .Status       D D ; Boolean properties of the object in SssEncoding.
                      ; +30h.
    .OrgLow       D D ; Origin pointer {offset of $}. Reset to .Bottom at start of each pass.                                                  `
    .OrgHigh      D D ; Member .Org is misused at link-time for file address of segment contents.
    .TopLow       D D ; Top limit of section/segment. Maximum of .Org ever reached. Not reset at start of pass.
    .TopHigh      D D ; SSS.Top-SSS.Bottom is object's virtual size. Emitted size is in .EmitBuffer, invalid in BSS and STRUC.
                      ; +40h.
    .PgmPool      D D ; ^POOL where structure-member names will be stored persistently.
    .EmitBuffer   D D ; ^BUFFER with emitted code/data.
    .RelocBuffer  D D ; ^BUFFER with RELOC records.
    .SssOrdBuffer D D ; ^BUFFER with DWORD pointers to ordered section (if sssSegment), or
                      ; ^BUFFER with DWORD pointers to ordered segments (if sssGroup), or
                      ; ^BUFFER with MEMBER records (if sssStructure). Otherwise 0.
                      ; +50h.
    .Alignment    D D ; Segment's own alignment specified by SEGMENT ALIGN=. Power of 2.
    .SegmIndex    D D ; Ordinal of this segment in COFF|ELF section header table or in OMF SGMDEF|GRPDEF record.
    .NameIndex    D D ; Ordinal of symbol of this segment in COFF|ELF SymbolTable|OMF LNAMES ordinal.
    .sh_link      D D ; Value of PFELF_SHDR.sh_link.
                      ; +60h.
    .sh_info      D D ; Value of PFELF_SHDR.sh_info.
    .RelocatedSss D D ; ^SSS which is relocated by this [.rel.name] ELF section.
    .SVA          EQU .RelocatedSss ; SVA used in PgmLink and RelocResolve. Model dependend.
    .BottomFA     D D ; Aligned file address of raw data.
    .TopFA        D D ; File address of end of raw data.
     ENDSTRUC SSS    ; +70h.

↑ 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. It may be set together with sssSegment.
    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. It may be set together with sssExtern.
    sssImagePrefix    EQU 0x0000_0040 ; Pseudosegment which represents PSP in COM|BOOT format (0x0100|0x7C00 bytes).
    sssOrdered        EQU 0x0000_0200 ; Already stored in PgmOrderSegments.
    sssDontCare       EQU 0x0000_0400 ; Ignore whether the segment is initialized or not.
    sssNotBSS         EQU 0x0000_0800 ; Emited content of the object is initialized. Reset in noninitialized sections.
                      ; COMBINE= property of segment.
    sssPublic         EQU 0x0000_1000 ; Segment will be combined with other segments of 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.
                      ; Assembly properties.
    sssImplicit       EQU 0x0010_0000 ; Group/segment was created by default in PgmCreateProgram.
    sssDefinedInPass  EQU 0x0020_0000 ; Object was already declared in this pass. Reset in PassCreate.
    sssDefinedInGroup EQU 0x0040_0000 ; Object was declared in GROUP pseudoinstruction.
    sssUsed           EQU 0x0080_0000 ; Structure/section/segment was referred at least once (sssDirty).
                      ; 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.
    ;                 EQU 0x0800_0000 ; Not used.
                      ; Linker properties.
    sssMarshaled      EQU 0x1000_0000 ; Section was already marshaled (combined) to its base segment.
    sssCombined       EQU 0x2000_0000 ; Segment|group from linked module was already combined into the main program.
    sssLinked         EQU 0x4000_0000 ; Segment|group bottom is fixed inside ImageBase frame.
    sssResolved       EQU 0x8000_0000 ; This external pseudosegment is resolved (replaced with the segment of matching public symbol).

↑ 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 \ Purposes of PE-COFF special directories 1..16
            EXPORT, IMPORT, RESOURCE, EXCEPTION, SECURITY, BASERELOC, DEBUG, COPYRIGHT,  \
            GLOBALPTR, TLS, LOAD_CONFIG, BOUND_IMPORT, IAT, DELAY_IMPORT, CLR, RESERVED, \
            \ General purposes 17..23
            CODE, DATA, RODATA, LITERAL, BSS, STACK, DRECTVE, \
            \ ELF special section purposes 24..32
            PHDR, SYMBOLS, DYNAMIC, STRINGS, RELOC, GOT, PLT, HASH, INTERP

↑ 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 sssPurposeRODATA = 0x0004_0000 sssPurposeLITERAL = 0x0008_0000 sssPurposeBSS = 0x0010_0000 sssPurposeSTACK = 0x0020_0000 sssPurposeDRECTVE = 0x0040_0000 sssPurposePHDR = 0x0080_0000 sssPurposeSYMBOLS = 0x0100_0000 sssPurposeDYNAMIC = 0x0200_0000 sssPurposeSTRINGS = 0x0400_0000 sssPurposeRELOC = 0x0800_0000 sssPurposeGOT = 0x1000_0000 sssPurposePLT = 0x2000_0000 sssPurposeHASH = 0x4000_0000 sssPurposeINTERP = 0x8000_0000

     %Value %SETA 1
     PURPOSE %FOR %SssPurposeList
        sssPurpose%PURPOSE EQU %Value
        %Value %SETA %Value << 1                                               ; >>
      %ENDFOR PURPOSE
    sssPurposeNONE          = 0x0000_0000
    ; Mask of all possible segment purposes:
    sssPurposeAny           = 0xFFFF_FFFF
    ; Special purposes of .DataDirectory in PE optional header:
    sssPurposeOptionalMask  = 0x0000_FFFF ; EXPORT,IMPORT,RESOURCE,...
    ; Special purposes of ELF-based formats: PHDR, SYMBOLS, DYNAMIC, STRINGS, RELOC, GOT, PLT, HASH, INTERP:
    sssPurposeElfMask       = 0xFF80_0000
    ; Special purpose of PSP in COM|BOOT format:
    sssPurposePSP           = sssPurposePHDR
    ; Regular (ordinary) purposes: CODE, RODATA, DATA, BSS, STACK:
    sssPurposeRegular       = sssPurposeCODE|sssPurposeDATA|sssPurposeRODATA|sssPurposeBSS|sssPurposeSTACK
    ; Purposes of segments with initialized contents:
    sssPurposeInitMask      = ~ (sssPurposeBSS|sssPurposeSTACK|sssPurposePSP)

      ENDHEAD sss ; End of module interface.

↑ SssFindByName SssType, SssCombine, NamePtr, NameSize, ProgPtr
  Procedure SssFindByName 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 SssFindByName sssStructure, 0, ESI, ECX, 0
Invokes
  PgmGetCurrent
Invoked by
  ExpEval ExpEvalIdentifier ExpParseDatatype PfDrectveCreate PfDrectveDestroy PfmzDefaultStack PgmListLiterals PseudoGROUP PseudoNoOperation PseudoSEGMENT PseudoSTRUC SssCombine SssCreate@LT SssCreate@RT SssCreateExtern SssOrderSections

    SssFindByName 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 SssFindByName

↑ SssFindByIndex SegmIndex, ProgPtr
  Procedure SssFindByIndex will search the module specified by %ProgPtr for an SSS segment with given SSS.SegmIndex.
Input
  SegmIndex is ordinal number of the segment in COFF|ELF symbol table.
  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 or if SegmIndex=0.
Invokes
  PgmGetCurrent
Invoked by
  PfcoffLoadModule PfelfLoadPgm

    SssFindByIndex Procedure SegmIndex, ProgPtr
        XOR EAX,EAX
        MOV ECX,[%SegmIndex]
        STC
        JECXZ .90:
        MOV EAX,[%ProgPtr]
        TEST EAX
        JNZ .10:
        Invoke PgmGetCurrent::
        JC .90:
    .10:MOV EAX,[EAX+PGM.SssList]
        ListGetFirst EAX
    .30:STC
        JZ .90:
        CMP [EAX+SSS.SegmIndex],ECX
        JE .90:
        ListGetNext EAX
        JMP .30:
    .90:MOV [%ReturnEAX],EAX
       EndProcedure SssFindByIndex

↑ SssFindByPurpose SssType, Purpose, Initialized, GroupPtr, ProgPtr, OutBuffer
  Procedure SssFindByPurpose will search the module specified by %ProgPtr for an SSS segment with given Purpose and store pointers to all complying objects to OutBuffer.
  Object with `sssOrdered` flag set are ignored. Stored objects are flagged `sssOrdered`.
Input
  SssType is either `sssGroup` or `sssSegment`.
  Purpose is desired flag from SssPurposeEnc.
  Initialized is either 0 for BSS segments, or sssNotBSS for initialized segments, or sssDontCare.
  GroupPtr is pointer to SSS group to which the searched segment belongs, or 0 (nongrouped segment). This argument is ignored when SssType=sssGroup.
  ProgPtr is pointer to PGM whose segments are searched.
  OutBuffer is pointer to BUFFER for pointers to the objects which were found. It will be emptied first.
Output
  ZF=0, ESI,ECX= contents of OutBuffer.
  ZF=1, ECX=0, ESI=undefined when no object was found.
Error
  -
Example
  Invoke SssFindByPurpose, sssSegment, sssPurposeRELOC, sssNotBSS, EAX, EBX, [%OutBuf]
See also
  SssGetSegm.
Invoked by
  PfcomCompile PfelfCompile PfelfSssSeg PfelfsoCompile PfpeOptionalHeader

    SssFindByPurpose Procedure SssType, Purpose, Initialized, GroupPtr, ProgPtr, OutBuffer
        MOV ESI,[%SssType]
        MOV EDX,[%Purpose]
        MOV ECX,[%Initialized]
        MOV EDI,[%GroupPtr]
        MOV EAX,[%ProgPtr]
        MOV EBX,[%OutBuffer]
        BufferClear EBX
        ListGetFirst [EAX+PGM.SssList]
        JZ .80:
    .10:JNSt [EAX+SSS.Status],ESI,.70:
        JNSt [EAX+SSS.Purpose],EDX,.70:
        JSt [EAX+SSS.Status],sssGroup,.30:
        CMP [EAX+SSS.GroupPtr],EDI
        JNE .70:
    .30:JSt ECX,sssDontCare,.50:            ; Use this segment.
        JECXZ .40:
        JNSt [EAX+SSS.Status],ECX,.70:
        JMP .50
    .40:JSt [EAX+SSS.Status],sssNotBSS,.70:
    .50:BufferStoreDword EBX,EAX
    .70:ListGetNext EAX
        JNZ .10:
    .80:BufferRetrieve EBX
        MOV [%ReturnESI],ESI
        MOV [%ReturnECX],ECX
        TEST ECX                            ; Set ZF when no SSS object was found.
       EndProcedure SssFindByPurpose

↑ 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 other than symSe was defined in it.
  Segment is dirty if any of its section is dirty.
  Group is dirty if at least one dirty 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
Invokes
  SssCheckDirty

    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 defined or 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 it as used, thus the next time SssCheckDirty will know faster.
          STC
          JMP .90:
    .40:  ; Section is dirty if symbols belongs to it.
          ListGetFirst [EDI+PGM.SymList]
          JZ .55:                                    ; If no symbol was emitted to the section EBX yet.
    .45:  JSt [EAX+SYM.Status],symSe,.50:            ; Segment symbols do not count.
          MOV ECX,[EAX+SYM.Section]
          JECXZ .50:
          CMP ECX,EBX
          JE .48:
          MOV ECX,[ECX+SSS.SegmPtr]
          JECXZ .50:
          CMP ECX,EBX
          JNE .50:
    .48:  JSt [EAX+SYM.Status],symDefined|symDefInPass,.Dirty: ; Forward-referenced symbols do not count.
    .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 section EAX belong to the inspected segment EBX?
          JNE .65:                                   ; Skip when 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 EAX belong to the inspected group?
          JNE .80:
          Invoke SssCheckDirty,EAX,EDI               ; Group is dirty if any of its segment is dirty.
          JC .Dirty:
    .80:  ListGetNext EAX
          JNZ .75:
    .NotDirty:
          CLC
    .90: EndProcedure SssCheckDirty

↑ SssGuessPurpose Segment
  SssGuessPurpose will analyze A) class `SSS.Class` and B) `SSS.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 chosen.
  4. If the name contains 'STACK', sssPurposeSTACK is chosen.
  5. If the name contains 'BSS' or 'UDATA', sssPurposeBSS is chosen.
  6. If the name contains 'RODATA' or 'RDATA', sssPurposeRODATA is chosen.
  7. If the name contains 'DATA', sssPurposeDATA is chosen.
  8. If the name contains 'CODE' or 'TEXT', sssPurposeCODE is chosen.
  9. 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 PseudoNoOperation 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.
        ; Output: Guessed purpose (or 0) is written to [EBX+SSS.Purpose]. EAX,ECX,EDX,ESI,EDI clobbered.
        TEST ECX
        JZ .90: ; If name empty.
        XOR EAX,EAX
        CMP [EBX+SSS.Purpose],EAX
        JNZ .90: ; If purpose was already set.
        Invoke EaBufferReserve::, SssGuessPurpose
        MOV EDX,EAX
        BufferStore EDX,ESI,ECX
        BufferRetrieve EDX
        MOV EDI,ESI
    .10:LODSB    ; Convert the name to upper case.
        CMP AL,'a'
        JB .14:
        CMP AL,'z'
        JA .14:
        SUB AL,'a'-'A'
    .14:STOSB
        LOOP .10:
        ; Search for string 'DRECTVE'.
        BufferRetrieve EDX
        MOV EDI,ESI ; EDI,ECX is now the uppercased name.
    .18:MOV AL,'D'
        REPNE SCASB
        JNE .22:
        CMP ECX,6
        JB .22:
        CMPD [EDI],'RECT'
        JNE .18:
        CMPW [EDI+4],'VE'
        JNE .18:
        MOV EAX,sssPurposeDRECTVE
        JMP .86:
    .22:; Search for string 'STACK'.
        BufferRetrieve EDX
        MOV EDI,ESI ; EDI,ECX is now the uppercased name.
    .26:MOV AL,'S'
        REPNE SCASB
        JNE .30:
        CMP ECX,4
        JB .30:
        CMPD [EDI],'TACK'
        MOV EAX,sssPurposeSTACK ; If 'STACK' found in the name.
        JE .86:
        JMP .26:
    .30:; Search for string 'BSS'.
        BufferRetrieve EDX
        MOV EDI,ESI ; EDI,ECX is now the uppercased name.
    .34:MOV AL,'B'
        REPNE SCASB
        JNE .38:
        CMP ECX,2
        JB .38:
        CMPW [EDI],'SS'
        MOV EAX,sssPurposeBSS ; If 'BSS' found in the name.
        JE .86:
        JMP .34:
    .38:; Search for string 'RODATA'.
        BufferRetrieve EDX
        MOV EDI,ESI ; EDI,ECX is now the uppercased name.
    .42:MOV AL,'R'
        REPNE SCASB
        JNE .46:
        CMP ECX,5
        JB .46:
        CMPD [EDI],'ODAT'
        JNE .42:
        CMPB [EDI+4],'A'
        MOV EAX,sssPurposeRODATA ; If 'RODATA' found in the name.
        JE .86:
        JMP .42:
    .46:; Search for string 'RDATA'.
        BufferRetrieve EDX
        MOV EDI,ESI ; EDI,ECX is now the uppercased name.
    .48:MOV AL,'R'
        REPNE SCASB
        JNE .50:
        CMP ECX,6
        JB .50:
        CMPD [EDI],'DATA'
        MOV EAX,sssPurposeRODATA ; If 'RDATA' found in the name.
        JE .86:
        JMP .48:
    .50:; Search for string 'UDATA'.
        BufferRetrieve EDX
        MOV EDI,ESI ; EDI,ECX is now the uppercased name.
    .54:MOV AL,'U'
        REPNE SCASB
        JNE .58:
        CMP ECX,4
        JB .58:
        CMPD [EDI],'DATA'
        MOV EAX,sssPurposeBSS ; If 'UDATA' found in the name.
        JE .86:
        JMP .54:
    .58:; Search for string 'DATA'.
        BufferRetrieve EDX
        MOV EDI,ESI ; EDI,ECX is now the uppercased name.
    .62:MOV AL,'D'
        REPNE SCASB
        JNE .66:
        CMP ECX,3
        JB .66:
        CMPD [EDI-1],'DATA'
        MOV EAX,sssPurposeDATA ; If 'DATA' found in the name.
        JE .86:
        JMP .62:
    .66:; Search for string 'CODE'.
        BufferRetrieve EDX
        MOV EDI,ESI ; EDI,ECX is now the uppercased name.
    .70:MOV AL,'C'
        REPNE SCASB
        JNE .74:
        CMP ECX,3
        JB .74:
        CMPD [EDI-1],'CODE'
        MOV EAX,sssPurposeCODE ; If 'CODE' found in the name.
        JE .86:
        JMP .70:
    .74:; Search for string 'TEXT'.
        BufferRetrieve EDX
        MOV EDI,ESI ; EDI,ECX is now the uppercased name.
    .78:MOV AL,'T'
        REPNE SCASB
        JNE .82:
        CMP ECX,3
        JB .82:
        CMPD [EDI-1],'TEXT'
        MOV EAX,sssPurposeCODE ; If 'TEXT' found in the name.
        JE .86:
        JMP .78:
    .82:XOR EAX,EAX ; No known purpose guessed.
    .86:SetSt [EBX+SSS.Purpose],EAX
        Invoke EaBufferRelease::,EDX
    .90:RET
       ENDP1 .Guess:

      CMPD [EBX+SSS.Purpose],0 ; 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
See also
  SssFindByPurpose.
Invoked by
  IiAssemble PseudoData PseudoNoOperation PseudoPROC StmParse

    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

↑ 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 PgmStatus=pgmLastPass,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 the current program.
  The section name is one of `@LT1 @LT2 @LT4 @LT8 @LT16 @LT32 @LT64` . It will be created when didn't exist yet. The segment to which the literal section belongs is selected with these rules:
  1. the last segment with explicit purpose LITERAL and purpose RODATA or DATA
  2. if no LITERAL segment exists, the last segment with purpose RODATA is chosen
  3. if no RODATA segment exists, the last segment with purpose DATA is chosen
  4. if no DATA|RODATA segment exists, an implicit one `@LT` will be created with purpose RODATA+LITERAL.
  Once the segment is specified, it will be marked with PURPOSE=LITERAL, so the next invokation goes faster.
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 SssCreateSe SssFindByName
Invoked by
  SymCreateLiteral

    SssCreate@LT Procedure DataType, Stm
    SssName LocalVar Size=8                          ; Room for string "@LT"+decimal number.
         SUB ECX,ECX                                 ; Initialization of %SssName.
         LEA EDI,[%SssName]
         MOV EAX,"@LTx"
         STOSD
         XOR EAX,EAX
         STOSD
         MOV [%ReturnEAX],EAX
         Invoke ExpWidthOfDataType::,[%DataType]     ; Find 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 SssFindByName,sssSection,0,ESI,ECX,0 ; Try to find [@LT*] section by name.
         MOV [%ReturnEAX],EAX
         JC .25:                                     ; Skip when [@LT*] 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:                                    ; Segment or section @LT* was found.
     .25: ; New literal section will be created.  ESI,ECX=Name, EBX=alignment (1,2,4,8,16,32,64).
         MOV EDI,[%Stm]
         MOV EDX,[EDI+STM.Program]
         ListGetLast [EDX+PGM.SssList]               ; 1.pass: find the last segment with purpose LITERAL & (DATA|RODATA).
         JZ .70:
     .30:JNSt [EAX+SSS.Status],sssSegment,.35:
         JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.35:
         JSt  [EAX+SSS.Purpose],sssPurposeRODATA|sssPurposeDATA,.80:
     .35:ListGetPrev EAX
         JNZ .30:
         ListGetLast [EDX+PGM.SssList]               ; 2.pass: find the last RODATA segment.
     .40:JNSt [EAX+SSS.Status],sssSegment,.45:
         JSt [EAX+SSS.Purpose],sssPurposeRODATA,.80:
     .45:ListGetPrev EAX
         JNZ .40:
         ListGetLast [EDX+PGM.SssList]               ; 3.pass: find the last DATA segment.
     .50:JNSt [EAX+SSS.Status],sssSegment,.60:
         JSt [EAX+SSS.Purpose],sssPurposeDATA,.80:
     .60:ListGetPrev EAX
         JNZ .50:
     .70: ; No data segment found, we have to create one. Its name will be [@LT].
         MOV EAX,pgmoptWidthMask
         AND EAX,[EDX+PGM.Pgmopt.Status]             ; Section width will be taken from program width.
         OR EAX,sssSegment+sssPrivate+sssNotBSS+sssImplicit+sssUsed
         LEA EDI,[%SssName]
         Invoke SssCreateSe,[%Stm],0,EDI,3,EAX,sssPurposeRODATA+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 other 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
         Invoke SssCreateSe,[%Stm],EDI,ESI,ECX,EAX,sssPurposeRODATA+sssPurposeLITERAL,EBX
         MOV [%ReturnEAX],EAX
    .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.
  When no code segment exists yet, an 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
  SssCreateSe SssFindByName
Invoked by
  PseudoPROC1 SymCreateLiteral

    SssCreate@RT Procedure PreviousSect, Stm
    SssName LocalVar Size=8                          ; Room for string "@RT"+decimal number.
          LEA EDI,[%SssName]                         ; Initialization os %SssName.
          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:                                   ; Skip when PreviousSect does not start with "@RT".
          SUB ECX,3                                  ; Previous (current) section already is runtime, select the next one.
          ADD ESI,3                                  ; Skip "@RT" in section name.
          LodD ESI,Size=ECX
          INC EAX
          MOV EDX,EDI                                ; Temporary save pointer to the 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 SssFindByName,sssSection,0,EDI,EDX,0 ; Try to find section by name.
          MOV [%ReturnEAX],EAX
          JC .30:                                    ; Skip when [@RT*] 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 @RT* 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]              ; 1.pass: find the last segment with purpose CODE & LITERAL.
          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]              ; 2.pass: find the last segment with purpose CODE.
     .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, we have to create one. Its name will be [@RT].
          MOV EAX,pgmoptWidthMask
          AND EAX,[ECX+PGM.Pgmopt.Status]            ; Segment width will be taken from program width.
          OR EAX,sssSegment+sssPrivate+sssNotBSS+sssImplicit+sssUsed
          Invoke SssCreateSe,[%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 other literals.
          ; New runtime literal section will be created in code segment EBX. EDI,EDX=Name, ECX=PGM.
          MOV EAX,[EBX+SSS.Status]
          AND EAX,sssCombineMask|sssWidthMask        ; Properties inherited from the segment EBX.
          OR EAX,sssSection+sssNotBSS+sssImplicit+sssUsed
          Invoke SssCreateSe,[%Stm],EBX,EDI,EDX,EAX,sssPurposeCODE+sssPurposeLITERAL,1
          MOV [%ReturnEAX],EAX
     .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

    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
  SssFindByName
Invoked by
  PassDestroy PfDrectveDestroy PfcoffLoadModule PfelfLoadPgm PfomfLoadModule PgmCreateImportModule SymCreate SymDynamicLink

    SssCreateExtern Procedure SymbolPtr, ProgramPtr
        MOV EBX,[%SymbolPtr]
        JNSt [EBX+SYM.Status],symExtern|symImport,.90:
        MOV EDX,[%ProgramPtr]
        Invoke SssFindByName,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
  PfcoffSectHeaders 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:
    .DYNAMIC:
    .GOT:
      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:
    .PHDR:
    .SYMBOLS:
    .STRINGS:
    .RELOC:
    .INTERP:
      RET
    .DRECTVE:
      OR EDX,pfcoffSCN_MEM_DISCARDABLE+pfcoffSCN_LNK_INFO+pfcoffSCN_LNK_REMOVE
      RET
    .IMPORT:
    .PLT:
      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_MEM_DISCARDABLE+pfcoffSCN_MEM_SHARED
    .RODATA:
    .HASH:
      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

↑ SssResizeGroup Group, Program
  SssResizeGroup recalculates VA, SVA, FA file size of the group, considering all segments of the program which belong to this group.
  Special ELF groups [PHDR] and [INTERP] are not affected by this procedure.
  `Group.Bottom` will be the lowest bottom and `Group.Top` the highest top of all its segments.
  Similary, `Group.SVA` will be the lowest segment virtual address of all its segments, and `Group.BottomFA` will be the lowest file bottom and `Group.TopFA` the highest file top of all its segments.
  SssResizeGroup will also update group's `SSS.Status:sssNotBSS` flag. It is set when at least one its segment has initialized contents.
  It also updates group's purpose by merging purposes of all its segments.
Input
  Group is pointer to the SSS group which is updated.
  Program is pointer to the linked program PGM where the Group belongs.
Output
  Group.Bottom(FA,SVA) and Group.Top(FA) are recalculated, .Status and .Purpose updated..
Error
  Errors are reported with macro Msg.
Invoked by
  PgmCombine PgmGroupByModel PgmResizeGroups

    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.TopFA],EAX
        MOV [EDI+SSS.TopLow],EAX
        MOV [EDI+SSS.TopHigh],EAX
        NOT EAX
        MOV [EDI+SSS.SVA],EAX
        MOV [EDI+SSS.BottomFA],EAX
        MOV [EDI+SSS.BottomLow],EAX
        MOV [EDI+SSS.BottomHigh],EAX
        RstSt [EDI+SSS.Status],sssNotBSS
        SetSt [EDI+SSS.Status],sssLinked             ; Assume that all segments of group EDI are already linked.
        ListGetFirst [EBX+PGM.SssList]               ; Walk through all segments.
    .10:MOV ECX,EAX
        JNSt [ECX+SSS.Status],sssSegment,.80:
        MOV EAX,[ECX+SSS.GroupPtr]
        CMP EDI,EAX                                  ; Does the segment ECX belong to group EDI?
        JE .20:
        TEST EAX
        JZ .80:                                      ; Ignore ungrouped segments.
        MOV EAX,[EAX+SSS.GroupPtr]                   ; EAX might be a group from linked module, refering to a group in base program.
        CMP EDI,EAX                                  ; Does the segment ECX belong to group EDI?
        JNE .80:
    .20:; ECX is a segment of group EDI. Let's adjust purpose, status and group's .Bottom and .Top.
        JNSt [ECX+SSS.Status],sssNotBSS,.30:         ; Group is sssNotBSS if any of segment is sssNotBSS.
        SetSt [EDI+SSS.Status],sssNotBSS
    .30:JSt [ECX+SSS.Status],sssLinked,.35:          ; Group is sssLinked if all its segments are sssLinked.
        RstSt [EDI+SSS.Status],sssLinked
    .35:MOV EAX,[ECX+SSS.Purpose]
        OR [EDI+SSS.Purpose],EAX
        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 RVA of its bottom was updated.
    .50:MOV EAX,[ECX+SSS.BottomFA]
        CMP EAX,[EDI+SSS.BottomFA]
        JAE .55:
        MOV [EDI+SSS.BottomFA],EAX                   ; Group FA of its bottom was updated.
    .55:MOV EAX,[ECX+SSS.SVA]
        CMP EAX,[EDI+SSS.SVA]
        JAE .60:
        MOV [EDI+SSS.SVA],EAX                        ; Group SVA was updated.
    .60:MOV EDX,[ECX+SSS.TopHigh]
        MOV EAX,[ECX+SSS.TopLow]
        CMP EDX,[EDI+SSS.TopHigh]
        JB .70:
        JA .65:
        CMP EAX,[EDI+SSS.TopLow]
        JBE .70:
    .65:MOV [EDI+SSS.TopLow],EAX
        MOV [EDI+SSS.TopHigh],EDX                    ; Group RVA of its top was updated.
    .70:MOV EAX,[ECX+SSS.TopFA]
        CMP EAX,[EDI+SSS.TopFA]
        JBE .80:
        MOV [EDI+SSS.TopFA],EAX                      ; Group FA of its top was updated.
    .80:ListGetNext ECX                              ; The next segment.
        JNZ .10:
        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, revert group .Bottom and .Top back to 0.
        MOV [EDI+SSS.BottomLow],EAX
        MOV [EDI+SSS.BottomHigh],EAX
        MOV [EDI+SSS.BottomFA],EAX
        MOV [EDI+SSS.SVA],EAX
    .90:EndProcedure SssResizeGroup

↑ SssCombine SssObject, BasePgm

  SssCombine is invoked when `BasePgm.ModulePgmList` contains one or more modules which were loaded into the BasePgm source due to pseudoinstruction LINK.
  Groups and external pseudosegments from those modules are copied (merged) into the BasePgm, their COMBINE= method is ignored.

  When the combined SssObject is a public segment, this procedure will combine it with the homonymous segment if it exists in the BasePgm. Emited contents of combined public segment is appended to the homonymous object in BasePgm and its `SSS.Bottom` is elevated. Relocations are just copied verbatim but not fixed yet.
  Segments with nonpublic COMBINE property are combined according to the table below.

  When a common segment has `COMBINE=COMMON`, it is similary related to the homonymous common segment in BasePgm but its `SSS.Bottom` is not elevated and it's left at 0. `SSS.Top` is set to the greatest of all common homonymous segments.

  Private segments are copied from modules to the BasePgm.

  `SSS.Status` of all combined objects is marked as `sssCombined`.

  `SSS.SegmPtr` of the combined segment in modules is updated to point at the target segment in BasePgm.

  Implicit symbols (`symSe` associated with each segment and group) of combined module are also combined to the BasePgm and `SYM.SymbPtr` points to their counterpart in BasePgm.

  Combine rules for objects with matching names
  SSS object types | sssExtern | sssGroup | sssSegment +sssPublic | sssSegment +sssStack | sssSegment +sssCommon | sssSegment +sssPrivate
  --- | --- | --- | --- | --- | --- | ---
  sssExtern | COMMON | PRIVATE | PRIVATE | PRIVATE | PRIVATE | PRIVATE
  sssGroup | PRIVATE | COMMON | PRIVATE | PRIVATE | PRIVATE | PRIVATE
  sssSegment+sssPublic | PRIVATE | PRIVATE | PUBLIC | PUBLIC | PUBLIC | PRIVATE
  sssSegment+sssStack | PRIVATE | PRIVATE | PUBLIC | PUBLIC | PUBLIC | PRIVATE
  sssSegment+sssCommon | PRIVATE | PRIVATE | PUBLIC | PUBLIC | COMMON | PRIVATE
  sssSegment+sssPrivate | PRIVATE | PRIVATE | PRIVATE | PRIVATE | PRIVATE | PRIVATE

Input
  SssObject is pointer to SSS object (segment or group) from the linked module.
  BasePgm is pointer to PGM - program which is the SssObject combined (copied or merged) to.
Output
  SssObject is combined to the BasePgm.
Invoked by
  PgmCombine
Invokes
  ExpAlign SssFindByName

    SssCombine Procedure SssObject, BasePgm
    AlignStuff LocalVar               ; Alignment stuff 0x00 or 0x90 between publicly combined segments.
        MOV ESI,[%SssObject]
        MOV EBX,[%BasePgm]
        XOR EDI,EDI
        MOV EAX,[ESI+SSS.Status]
        JSt EAX,sssCombined,.90:      ; When the object is already combined, do nothing.
        SetSt [ESI+SSS.Status],sssCombined
        AND EAX,sssSegment|sssExtern|sssGroup
        Dispatch EAX,sssSegment,sssExtern,sssGroup
        JMP .90:                      ; Ignore other SSS types (actually no section or structure can pass thru PgmLoad).

    .sssGroup:                        ; Homonymous groups always merge as COMMON. They have no emitted and reloc contents.
     ;; JSt [EBX+PGM.Pgmopt.Status],pgmoptDynamic,.90: ; Do not combine groups from ELFSO modules.
        Invoke SssFindByName,sssGroup,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX ; Search for the same group in BasePgm.
        JNC .10:
        ListStore [EBX+PGM.SssList],ESI ; Group ESI defined in linked module wasn't found in BasePgm EBX. Copy it there.
    .10:MOV [ESI+SSS.GroupPtr],EAX    ; Group EAX was found in BasePgm.
        MOV [ESI+SSS.SegmPtr],EAX     ; Combine abandoned group ESI to the group EAX.
        JMP .90:

    .sssExtern:                       ; Homonymous extern pseudosegments always merge as COMMON. They have no emitted and reloc contents.
        Invoke SssFindByName,sssExtern,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX ; Search for the same extern pseudosegment in BasePgm.
        JNC .20:
        ListStore [EBX+PGM.SssList],ESI ; sssExtern ESI defined in linked module wasn't found in BasePgm EBX. Copy it there.
    .20:MOV [ESI+SSS.SegmPtr],EAX
        JMP .90:

    .sssSegment:                      ; Homonymous segments combine according to their COMBINE= property.
        MOV EAX,sssCombineMask
        AND EAX,[ESI+SSS.Status]
        Dispatch EAX,sssPublic,sssCommon,sssStack
        JMP .PRIVATE:                 ; Private segment does not combine.

    .sssCommon:                       ; Segment ESI is COMBINE=COMMON.
        Invoke SssFindByName,sssSegment,sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
        JNC .COMMON:                  ; Only if both segments have COMBINE=COMMON.
        ; Homonymous COMMON segment was not found, try PUBLIC or STACK.
        Invoke SssFindByName,sssSegment,sssPublic|sssStack,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
        JNC .PUBLIC:
        JMP .PRIVATE:

    .sssStack:                        ; Segment ESI is COMBINE=STACK.
        Invoke SssFindByName,sssSegment,sssStack|sssPublic|sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
        JNC .PUBLIC:                  ;  Segments with stack-combine method link as public (they are concatenated).
        JMP .PRIVATE:

    .sssPublic:                       ; Segment ESI is COMBINE=PUBLIC.
        Invoke SssFindByName,sssSegment,sssPublic,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
        JNC .PUBLIC:                  ; Only if both segments have COMBINE=PUBLIC.
        ; Homonymous PUBLIC segment was not found, try COMMON or STACK.
    .75:Invoke SssFindByName,sssSegment,sssCommon|sssStack,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
        JNC .PUBLIC:
       ;JMP .PRIVATE:

       ; The actual combine method procedures are defined here.
       ; EBX=BasePgm, ESI=linked segment, EAX=homonymous segment in BasePgm or 0 when not found.

    .PRIVATE:   ; PRIVATE combining method. Combined segment ESI is copied to the BasePgm EBX. Error if a homonymous segment already exists there.
        Invoke SssFindByName,sssSegment,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
        JNC .E7862:                   ; Linked private segment [!1S] is already declared in program "!2S".
        ListStore [EBX+PGM.SssList],ESI
        MOV EDI,EAX                   ; EDI is the copy of segment in BasePgm.
        MOV [ESI+SSS.SegmPtr],EDI
        MOV EDX,[ESI+SSS.SymPtr]

        TEST EDX
        JZ .90:

        ListStore [EBX+PGM.SymList],EDX
        MOV [EAX+SYM.Section],EDI     ; EAX is the copy of symSe symbol in BasePgm.
        MOV [EDI+SSS.SymPtr],EAX
        TEST EDX
        JZ .90:
        MOV [EDX+SYM.SymbPtr],EAX     ; EDX is the abandoned symSe in loaded module.
        JMP .90:

    .E7862: Msg '7862',ESI,EBX        ; Linked private segment [!1S] is already declared in program "!2S".
        JMP .90:

    .COMMON:                          ; COMMON combining method. Both combined (ESI) and base (EAX) segments remain on common bottom=0.
        MOV EDI,EAX                   ; Homonymous segment which was found in BasePgm.
        CMPD [EDI+SSS.GroupPtr],0
        JNZ .C1:                      ; Keep the existing group of base segment.
        MOV EAX,[ESI+SSS.GroupPtr]    ; When nongrouped, assign it to the group of combined segment.
        MOV [EDI+SSS.GroupPtr],EAX
    .C1:MOV [ESI+SSS.SegmPtr],EDI     ; Let the abandoned combined segment ESI refer to the base segment EDI.
        MOV EDX,[ESI+SSS.TopHigh]     ; New base segment's 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 .90:

    .E8525:Msg '8525',[%SssObject]    ; Size of segment [!1S] exceeded 4 GB.
        JMP .90:

    .PUBLIC:                          ; PUBLIC combining method of combined (ESI) and base (EAX) segment.
        MOV EBX,EAX                   ; EBX is now the homonymous segment in BasePgm.
        MOV EDI,ESI                   ; EDI is now the combined (linked) segment.
        SetSt [EDI+SSS.Status],sssCombined
        MOV [EDI+SSS.SegmPtr],EBX     ; Let the combined segment EDI refer to the existing base segment EBX.
        CMPD [EBX+SSS.GroupPtr],0
        JNZ .P0:
        MOV EAX,[EDI+SSS.GroupPtr]    ; Copy group assignment from combined segment EDI to base segment EBX.
        MOV [EBX+SSS.GroupPtr],EAX
    .P0:MOV EAX,[EDI+SSS.Purpose]     ; Merge properties of public segment combination.
        OR  [EBX+SSS.Purpose],EAX
        XOR ECX,ECX                   ; Prepare alignment stuff in CL (0x00 or 0x90).
        JNSt EAX,sssPurposeCODE,.P1:
        MOV CL,0x90
    .P1:MOV [%AlignStuff],ECX
        MOV EAX,sssNotBSS
        AND EAX,[EDI+SSS.Status]
        OR  [EBX+SSS.Status],EAX      ; Merge initialized-data status.
        MOV EAX,[EDI+SSS.Alignment]
        CMP EAX,[EBX+SSS.Alignment]
        JNA .P2:
        MOV [EBX+SSS.Alignment],EAX   ; Use the greater alignment from both segments, if they are different.
    .P2:JNSt [EDI+SSS.Purpose],sssPurposeRegular,.P4: ; Width of literal|PE-special purpose segments is not checked.
        MOV EAX,sssWidthMask          ; Width of segments with standard purpose CODE|RODATA|DATA|BSS|STACK must match.
        MOV EDX,EAX
        AND EAX,[EDI+SSS.Status]      ; Width of linked segment (sssWidth16|32|64).
        JZ .P4:                       ; Segment from the combined segment may have unspecified width, this is OK.
        AND EDX,[EBX+SSS.Status]      ; Width of base segment.
        JNZ .P3:
        MOV EDX,EAX                   ; Base segment may have been created by PseudoGROUP with unspecified width, this is OK.
        OR [EBX+SSS.Status],EDX
    .P3:CMP EAX,EDX                   ; Check segment width match.
        JE .P4:
        Msg '7718',EDI                ; Cannot combine segments [!1S] which have different width.
        JMP .90:
    .P4:MOV EAX,[EBX+SSS.TopLow]      ; EAX=virtual unaligned size of base segment.
        Invoke ExpAlign::,EAX,[EDI+SSS.Alignment],0 ; Align base top according to the alignment of combined segment.
        ADD EAX,ECX                   ; Add alignment stuff size to the base top. EAX is now aligned bottom of linked segment EDI, i.e. its elevation.
        JC .E8525:                    ; Size of segment [!1S] exceeded 4 GB.
        JNSt [EBX+SSS.Status],sssNotBSS,.P5: ; Skip when uninitialized segments are combined.
        BufferRetrieve [EBX+SSS.EmitBuffer] ; Set the emitted size of base segment equal to its virtual size EAX.
        SUB ECX,EAX                   ; Align emitted contents of base segment EBX.
        NEG ECX                       ; ECX is now the nonnegative size of alignment stuff.
        BufferResize [EBX+SSS.EmitBuffer],ECX,Stuff=[%AlignStuff] ; EAX is the size of base segment EBX, also the new bottom of combined segment EDI.
    .P5: ; VA of segments EBX and EDI was 0 but VA of the appended segment EDI will be elevated by EAX (aligned size of segment EBX).
        ADD [EDI+SSS.BottomLow],EAX   ; Elevate VA of appended segment EDI. It will be used later to update symbols and relocations.
        ADD [EDI+SSS.TopLow],EAX
        JC .E8525:                    ; Size of segment [!1S] exceeded 4 GB.
        MOV EAX,[EDI+SSS.TopLow]
        MOV EDX,[EDI+SSS.TopHigh]
        MOV [EBX+SSS.TopLow],EAX      ; Update the top of base segment, which was appended with combined segment.
        MOV [EBX+SSS.TopHigh],EDX
        MOV ECX,[EDI+SSS.SymPtr]
        JECXZ .82:
        ADD [ECX+SYM.OffsetLow],EAX   ; Update symSe associated with combined segment EDI.
    .82:JNSt [EBX+SSS.Status],sssNotBSS,.90: ; There is no emitted contents and no relocations in BSS segment.
        BufferRetrieve [EDI+SSS.EmitBuffer]  ; Copy the emitted contents of combined segment EDI to the aligned base segment EBX.
        BufferStore [EBX+SSS.EmitBuffer],ESI,ECX
        BufferRetrieve [EDI+SSS.RelocBuffer] ; Copy relocations of combined segment EDI unchanged to the base segment EBX.
        BufferStore [EBX+SSS.RelocBuffer],ESI,ECX ; They will be elevated later in RelocCombine.
    .90: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.
Error
  F9314 is reported with macro Msg.
See also
  No-operation encoding
Invoked by
  PseudoALIGN 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.
        BufferResize EBX,ECX,Stuff=0x00
        JMP .90:
    .60:; Alignment stuff in code section.
        MOV EDX,9
        JECXZ .90:
        SUB ECX,EDX
        JB  .80:
        PUSH ECX
          CALL .Nop:
        POP ECX
        JC .90:
        JMP .60:
    .80:ADD EDX,ECX
        CALL .Nop:
    .90:EndProcedure SssEmitAlignment

    SssEmitAlignment.Nop: PROC ; Emit EDX bytes of NOP1..NOP9 to the buffer EBX. EDI=^SSS.
    [.data]   ; The following table defines offsets of NOP opcode in the 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 the real NOP opcode (ECX bytes long).
        BufferStore EBX,ESI,ECX
        Msg cc=C,'9314',SssEmitAlignment ; Allocation error storing to buffer in !1H.
        RET
        ENDP SssEmitAlignment.Nop:

↑ SssCreateGroup Stm, NamePtr,NameSize, SssStatus, SssPurpose
  SssCreateGroup adds a new SSS record on `Stm.Program.SssList` and initializes its .Name, .LinePtr, .GroupPtr, .Status.
  .Name is made program-persistent. LinePtr is taken from `Stm.LinePtr`.
Input
  Stm Statement which creates the object. `Stm.Program` must specify PGM object which the segment/section/strucutre is created for.
  NamePtr Pointer to the group name (without `[``]`).
  NameSize Size of object name,
  SssStatus is a combination of SSS.Status flags. sssGroup+sssDefinedInPass will be added here.
  SssPurpose is a combination of SSS.Purpose flags.
Output
  CF=0
  EAX= pointer to the created SSS group.
Errors
  CF=1
  EAX=0
Invoked by
  PfelfCompile PfelfsoCompile PfelfxCompile PgmGroupByModel PseudoGROUP SssCreateImplicit

    SssCreateGroup Procedure Stm, NamePtr, NameSize, SssStatus, SssPurpose
         MOV ECX,[%Stm]
         MOV EBX,[ECX+STM.Program]
         MOV EDI,[ECX+STM.LinePtr]
         MOV EDX,[EBX+PGM.Pool]
         ListNew [EBX+PGM.SssList],Zeroed=yes ; Allocate room for the new group.
    .10: MOV [%ReturnEAX],EAX
         JC .90:
         MOV [EAX+SSS.LinePtr],EDI
         MOV EDI,EAX                  ; EDI is now pointer to the new group.
         MOV ECX,[%NameSize]
         PoolStore EDX,[%NamePtr],ECX ; Make the Name permanent during PGM lifetime.
         JC .10:
         MOV [EDI+SSS.NamePtr],EAX    ; EAX,ECX is now nonvolatile group name.
         MOV [EDI+SSS.NameSize],ECX
         MOV [EDI+SSS.PgmPool],EDX
         BufferCreate EDX,Size=64
         MOV [EDI+SSS.SssOrdBuffer],EAX ; Buffer for the pointers to segments which belong to the group.
         MOV ECX,[%SssPurpose]
         MOV EDX,[%SssStatus]
         SetSt EDX,sssGroup+sssDefinedInPass
         JSt EDX,sssImplicit,.70:
         SetSt EDX,sssUsed
    .70: SetSt [EDI+SSS.Status],EDX
         SetSt [EDI+SSS.Purpose],ECX
         MOV [EDI+SSS.GroupPtr],EDI
     .90:EndProcedure SssCreateGroup  ; Group does not store emitted data, relocations, members.

↑ SssCreateSe Stm, SegmPtr, NamePtr,NameSize, SssStatus, Purpose, Alignment
  SssCreateSe creates a new segment or section. It will add a new SSS record on `Stm.Program.SssList`.
  `.Name` is made program-persistent. LinePtr is taken from `Stm.LinePtr`.
Input
  Stm Statement which creates the SSS object, e.g. `[NewSegment] SEGMENT` or `[NewSection]`.
  SegmPtr is pointer to SSS segment which the (literal) section is created in. 0 when segment is created.
  NamePtr Pointer to the section name (without `[``]`). Name may be volatile, it will be stored on program pool. It must be unique among other existing SSS objects.
  Section name is not delocalized even when it starts with `.`.
  NameSize Size of object name in bytes.
  SssStatus Combination of flags which describe the created SSS.
  Purpose Flag(s) defined in SssPurposeEnc.
  Alignment Alignment value (0,1,2,4,8,16...)
Output
  CF=0
  EAX= pointer to the created SSS section|segment.
Errors
  CF=1 if allocation failed.
See also
  SssCreate@LT SssCreate@RT
Invoked by
  PfDrectveCreate PfcoffLoadModule PfmzDefaultStack PfpeBaserelocCreate PfpeExportCreate PfpeImportCreate PfrsrcLoadPgm PseudoGROUP PseudoNoOperation PseudoSEGMENT SssCreate@LT SssCreate@RT SssCreateImplicit
Invokes
  SymCreateSe

    SssCreateSe Procedure Stm, SegmPtr, NamePtr, NameSize, SssStatus, Purpose, Alignment
         MOV ECX,[%Stm]
         MOV EBX,[ECX+STM.Program]
         MOV EDX,[EBX+PGM.Pool]
         MOV EDI,[ECX+STM.LinePtr]
         ListNew [EBX+PGM.SssList],Zeroed=yes ; Allocate room for the new section.
         MOV [%ReturnEAX],EAX
         JC .90:
         MOV [EAX+SSS.LinePtr],EDI
         MOV EDI,EAX                         ; EDI now points to the new segment.
         MOV ECX,[%NameSize]
         PoolStore EDX,[%NamePtr],ECX        ; Make the Name permanent during PGM lifetime.
         JC .90:
         MOV [EDI+SSS.NamePtr],EAX
         MOV [EDI+SSS.NameSize],ECX
         MOV [EDI+SSS.PgmPool],EDX
         BufferCreate EDX,Size=16K
         MOV [EDI+SSS.EmitBuffer],EAX
         BufferCreate EDX,Size=4K
         MOV [EDI+SSS.RelocBuffer],EAX
         MOV EAX,[%SssStatus]
         MOV [EDI+SSS.Status],EAX
         JNSt EAX,sssSegment|sssGroup,.50:
         BufferCreate EDX,Size=32
         MOV [EDI+SSS.SssOrdBuffer],EAX
    .50: MOV ECX,[%Alignment]
         MOV EDX,[%Purpose]
         MOV [EDI+SSS.Alignment],ECX
         MOV [EDI+SSS.Purpose],EDX
         MOV ECX,[%SegmPtr]                  ; ^SSS with segment where the section EDI belongs to.
         TEST ECX
         JNZ .70:
         MOV ECX,EDI                         ; Segment EDI points to itself if %SegmPtr is not specified.
    .70: MOV [EDI+SSS.SegmPtr],ECX
         Invoke SymCreateSe::,EDI,EBX        ; Create a symbol associated with the segment EDI.
     .90:EndProcedure SssCreateSe

↑ SssCreateStructure Stm, NamePtr,NameSize, SssStatus, Alignment
  SssCreateStructure 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, .SssOrdBuffer 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.
  When a segment is created, this procedure creates symbol of `symSe` type with the same name. It represent the bottom of segment for relocation purposes.
Input
  Stm ^STM which creates the structure.
  NamePtr Pointer to delocaled structure name.
  NameSize Size of object name.
  SssStatus Combination of flags which describe the created SSS. One of sssTypeMask must be set.
  Alignment Alignment value (0,1,2,4,8,16...)
Output
  CF=0
  EAX= pointer to the created SSS structure.
Errors
  CF=1, reported with macro Msg.
Invokes
  SymFindByName
Invoked by
  PseudoSTRUC

    SssCreateStructure Procedure Stm, NamePtr, NameSize, SssStatus, Alignment
         ; SSS.Name.
         MOV ECX,[%Stm]
         MOV EBX,[ECX+STM.Program]
         MOV EDI,[ECX+STM.LinePtr]
         MOV EDX,[EBX+PGM.Pool]
         ListNew [EBX+PGM.SssList],Zeroed=yes ; Allocate room for the new structure.
         MOV [%ReturnEAX],EAX
         JC .90:
         MOV [EAX+SSS.LinePtr],EDI
         MOV EDI,EAX                    ; EDI is now pointer to the new structure.
         MOV ECX,[%NameSize]
         PoolStore EDX,[%NamePtr],ECX   ; Make the Name permanent during PGM lifetime.
         JC .90:
         MOV [EDI+SSS.NamePtr],EAX      ; ESI,ECX is now nonvolatile nonlocal structure name.
         MOV [EDI+SSS.NameSize],ECX
         MOV [EDI+SSS.PgmPool],EDX
         MOV EAX,[%SssStatus]
         MOV ECX,[%Alignment]
         MOV [EDI+SSS.Status],EAX
         MOV [EDI+SSS.Alignment],ECX
         SetSt [EDI+SSS.Purpose],sssPurposeDATA
         MOV ECX,512                    ; Initial buffer size for structures.
         BufferCreate EDX,Size=ECX
         MOV [EDI+SSS.EmitBuffer],EAX
         BufferCreate EDX,Size=ECX
         MOV [EDI+SSS.RelocBuffer],EAX
         BufferCreate EDX,Size=ECX
         MOV [EDI+SSS.SssOrdBuffer],EAX
         MOVD [EDI+SSS.SegmIndex],-1 ; pfcoffSYM_ABSOLUTE
         ; The just defined strucure name might have been forward referenced sooner and thus incorrectly had created a symbol.
         Invoke SymFindByName::,0,[EDI+SSS.NamePtr],[EDI+SSS.NameSize],EBX
         JC .80:                        ; If it's not the case.
         JNSt [EAX+SYM.Status],symDefined,.40:
         JSt [EAX+SYM.Status],symSe,.80:
         Msg '6612',EAX,[EAX+SYM.LinePtr] ; Cannot declare structure "!1S" when such symbol was declared at !2@.
         JMP .90:
    .40: ; False created but not defined symbol EAX will be removed, because its name belongs to the structure.
         ListRemove [EBX+PGM.SymList],EAX
    .80: CLC
    .90:EndProcedure SssCreateStructure

↑ SssOrderSections Segment, Program
  This procedure decides how are subsections of each segment ordered.
  0. Base section with identical name
  1. Nonliteral code sections in the order as they were declared
  2. Literal code sections in alphabetical order (`[@RT0],[$RT1],[@RT2]...`)
  3. Nonliteral read-only data sections in the order as they were declared
  4. Literal read-only data sections in the decreasing alignment order (`[@LT64],[@LT32],,,[@LT1]`)
  5. Nonliteral data sections in the order as they were declared
  6. Literal data sections in the decreasing alignment order (`[@LT64],[@LT32],,,[@LT1]`)
  7. All other nonBSS sections in the order as they were declared
  8. BSS sections in the order as they were declared
  9. Stack sections in the order as they were declared
  Pointers to ordered sections are stored to `Segment.SssOrdBuffer` . Each stored section is marked as `sssMarshaled` to avoid duplications.
Input
  Segment is pointer to SSS - the segment whose sections are ordered. Program ^PGM.
Output
  `Segment.SssOrdBuffer` is filled with DWORD pointers to its SSS sections in the desired order. At least one pointer is always stored.
See also
  PgmOrderSegments
Manual
  Ordering of segments
Invoked by
  PgmLinkSections
Invokes
  SssFindByName
Tested by
  t1717 t2520

    SssOrderSections Procedure Segment, Program
    LitVal  LocalVar         ; Ordinal Nr of literal section 1,2,3..  or 64,32,16,8,4,2,1.
    LitName LocalVar Size=12 ; Room for section name "@RT1","@RT2","@RT3".. or "@LT64","@LT32".."@LT1".
         MOV EBX,[%Program]
         MOV EDI,[%Segment]
         MOV EDX,[EDI+SSS.SssOrdBuffer]
         BufferClear EDX

         ; 0. Base section, identical with segment EDI.
         BufferStoreDword EDX,EDI
         SetSt [EDI+SSS.Status],sssMarshaled

         ; 1. Non-literal sections of segment EDI.
         ListGetFirst [EBX+PGM.SssList]
    .11: JSt [EAX+SSS.Status],sssMarshaled,.19:
         JNSt [EAX+SSS.Status],sssSection,.19:
         CMP [EAX+SSS.SegmPtr],EDI                   ; Does the section EAX belong to Segment EDI?
         JNE .19:
         JSt [EAX+SSS.Purpose],sssPurposeLITERAL,.19:; Leave literal sections to next steps.
         JNSt [EAX+SSS.Purpose],sssPurposeCODE,.19:  ; Prefer nonliteral code sections.
         BufferStoreDword EDX,EAX
         SetSt [EAX+SSS.Status],sssMarshaled
    .19: ListGetNext EAX
         JNZ .11:

         ; 2. Literal code sections by name in alphabetical order.
         JNSt [EDI+SSS.Purpose],sssPurposeCODE,.30:  ; If segment EDI cannot host @RT sections.
         MOVD [%LitVal],0                            ; Start with [@RT0].
         MOVD [%LitName],'@RT0'
    .21: LEA ESI,[%LitName]
         MOV EAX,[%LitVal]
         LEA EDI,[ESI+3]                             ; Skip characters '@RT'.
         StoD EDI
         SUB EDI,ESI                                 ; Number of characters in literal section name.
         Invoke SssFindByName, sssSection,0,ESI,EDI,EBX
         MOV EDI,[%Segment]
         JNC .28:
         CMPD [%LitVal],0
         JE  .29:                                    ; After [@RT0] try to find [@RT1] etc.
         JMP .30:                                    ; If no section above @RT0 exists, neither the following one can exist.
    .28: JSt [EAX+SSS.Status],sssMarshaled,.29:
         CMP [EAX+SSS.SegmPtr],EDI
         JNE .29:                                    ; If the literal section doesn't belong to our segment.
         BufferStoreDword EDX,EAX
         SetSt [EAX+SSS.Status],sssMarshaled
    .29: INCD [%LitVal]                              ; Prepare for the next code literal section.
         JMP .21:

         ; 3. Nonliteral rodata sections.
    .30: JNSt [EDI+SSS.Purpose],sssPurposeRODATA,.40:
         ListGetFirst [EBX+PGM.SssList]
    .31: JSt [EAX+SSS.Status],sssMarshaled,.39:
         JNSt [EAX+SSS.Status],sssSection,.39:
         CMP [EAX+SSS.SegmPtr],EDI                   ; Does the section EAX belong to Segment EDI?
         JNE .39:
         JSt [EAX+SSS.Purpose],sssPurposeLITERAL,.39:; Leave literal sections to next steps.
         JNSt [EAX+SSS.Purpose],sssPurposeRODATA,.39:; Prefer nonliteral rodata sections.
         BufferStoreDword EDX,EAX
         SetSt [EAX+SSS.Status],sssMarshaled
    .39: ListGetNext EAX
         JNZ .31:

         ; 4. Read-only data literal sections in descending order of their alignment ([@LT64], [@LT32],..[@LT1]).
    .40: JNSt [EDI+SSS.Purpose],sssPurposeRODATA|sssPurposeDATA,.60: ; If segment EDI cannot host @LT sections.
         MOVD [%LitVal],64                           ; Start with @LT section which has the strongest alignment.
         MOVD [%LitName],'@LT*'
    .41: 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 literal section name.
         Invoke SssFindByName,sssSection,0,ESI,EDI,EBX
         MOV EDI,[%Segment]
         JC .49:                                     ; If no such section was present.
         JSt [EAX+SSS.Status],sssMarshaled,.49:
         CMP [EAX+SSS.SegmPtr],EDI
         JNE .49:                                    ; If the literal section doesn't belong to our segment.
         BufferStoreDword EDX,EAX
         SetSt [EAX+SSS.Status],sssMarshaled
    .49: SARD [%LitVal],1                            ; Prepare for the next data literal section.
         JNC .41:                                    ; CF=1 if [@LT1] has just been marshaled. Otherwise try the next @LT*.

         ; 5. Nonliteral data sections.
    .50: JNSt [EDI+SSS.Purpose],sssPurposeDATA,.60:
         ListGetFirst [EBX+PGM.SssList]
    .51: JSt [EAX+SSS.Status],sssMarshaled,.59:
         JNSt [EAX+SSS.Status],sssSection,.59:
         CMP [EAX+SSS.SegmPtr],EDI                   ; Does the section EAX belong to Segment EDI?
         JNE .59:
         JSt [EAX+SSS.Purpose],sssPurposeLITERAL,.59:; Leave literal sections to next step.
         JNSt [EAX+SSS.Purpose],sssPurposeDATA,.59:  ; Prefer nonliteral rodata sections.
         BufferStoreDword EDX,EAX
         SetSt [EAX+SSS.Status],sssMarshaled
    .59: ListGetNext EAX
         JNZ .51:

         ; 6. Data literal sections in descending order of their alignment ([@LT64], [@LT32],..[@LT1]).
    .60: JNSt [EDI+SSS.Purpose],sssPurposeRODATA|sssPurposeDATA,.70: ; If segment EDI cannot host @LT* sections.
         MOVD [%LitVal],64                           ; Start with @LT section which has the strongest alignment.
         MOVD [%LitName],'@LT*'
    .61: 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 literal section name.
         Invoke SssFindByName,sssSection,0,ESI,EDI,EBX
         MOV EDI,[%Segment]
         JC .69:                                     ; If no such section was present.
         JSt [EAX+SSS.Status],sssMarshaled,.69:
         CMP [EAX+SSS.SegmPtr],EDI
         JNE .79:                                    ; If the literal section doesn't belong to our segment.
         BufferStoreDword EDX,EAX
         SetSt [EAX+SSS.Status],sssMarshaled
    .69: SARD [%LitVal],1                            ; Prepare for the next data literal section.
         JNC .61:                                    ; CF=1 if [@LT1] has just been marshaled. Otherwise try the next @LT*.

    .70: ; 7. All other nonBSS sections which wasn't marshaled yet.
         ListGetFirst [EBX+PGM.SssList]
    .71: JSt [EAX+SSS.Status],sssMarshaled,.79:
         JNSt [EAX+SSS.Status],sssSection,.79:
         CMP [EAX+SSS.SegmPtr],EDI                   ; Does the section EAX belong to Segment EDI?
         JNE .79:
         JNSt [EAX+SSS.Status],sssNotBSS,.79:        ; Leave literal sections to next step.
         BufferStoreDword EDX,EAX
         SetSt [EAX+SSS.Status],sssMarshaled
    .79: ListGetNext EAX
         JNZ .71:

    .80: ; 8. All BSS sections.
         ListGetFirst [EBX+PGM.SssList]
    .81: JSt [EAX+SSS.Status],sssMarshaled,.89:
         JNSt [EAX+SSS.Status],sssSection,.89:
         CMP [EAX+SSS.SegmPtr],EDI                   ; Does the section EAX belong to Segment EDI?
         JNE .89:
         JNSt [EAX+SSS.Purpose],sssPurposeBSS,.89:
         BufferStoreDword EDX,EAX
         SetSt [EAX+SSS.Status],sssMarshaled
    .89: ListGetNext EAX
         JNZ .81:

    .90: ; 9. All other STACK sections.
         ListGetFirst [EBX+PGM.SssList]
    .91: JSt [EAX+SSS.Status],sssMarshaled,.99:
         JNSt [EAX+SSS.Status],sssSection,.99:
         CMP [EAX+SSS.SegmPtr],EDI                   ; Does the section EAX belong to Segment EDI?
         JNE .99:
         JNSt [EAX+SSS.Purpose],sssPurposeBSS,.99:
         BufferStoreDword EDX,EAX
         SetSt [EAX+SSS.Status],sssMarshaled
    .99: ListGetNext EAX
         JNZ .91:
        EndProcedure SssOrderSections

↑ SssLinkSections Segment
  SssLinkSections is invoked at the end of program assembly when ENDPROGRAM is encountered in the last pass. All sections of the Segment are virtually linked to it, i.e. their `SSS.Bottom` and `SSS.Top` is adjusted by virtual sizes of previous sections, in the order specified in `Segment.SssOrdBuffer`, aligned by section's `SSS.Alignment`.
  Section order must have been set by SssOrderSections. Unchanged emited contents and relocations is concatenated to Segment.
  SssLinkSections links VA only. SVA and FA is ignored.
See also
  PgmLink.
Input
  Segment = ^SSS segment whose sections are linked.
Output
  `SSS.Bottom` and `SSS.Top` of each section is adjusted. `SSS.Org` is reset to the new `SSS.Bottom`.
Errors
  reported with macro Msg.
Invokes
  ExpAlign
Invoked by
  PgmLinkSections
Tested by
  t2520

    SssLinkSections Procedure Segment
    OrdPtr LocalVar                                  ; Pointer to an array of pointers to sections.
    OrdTop LocalVar                                  ; Pointer behind the end of array.
         MOV EBX,[%Segment]
         BufferRetrieve [EBX+SSS.SssOrdBuffer]       ; Pass 1 - merge purposes of linked sections.
         ADD ECX,ESI                                 ; ESI..ECX is array of pointers to sections.
         MOV [%OrdPtr],ESI
         MOV [%OrdTop],ECX
    .01: MOV ESI,[%OrdPtr]
         CMP ESI,[%OrdTop]
         JNB .09:
         MOV EDI,[ESI]                               ; EDI is the linked section, EBX is its segment.
         ADDD [%OrdPtr],4                            ; Prepare for the next section.
         JNSt [EDI+SSS.Status],sssNotBSS,.03:        ; If any section is initialized, its segment EBX is too.
         SetSt [EBX+SSS.Status],sssNotBSS
    .03: MOV EAX,[EDI+SSS.Purpose]
         OR [EBX+SSS.Purpose],EAX                    ; Merge purposes of each section to its segment EBX.
         JMP .01:
    .09: BufferRetrieve [EBX+SSS.SssOrdBuffer]       ; Pass 2 - update virtual addresses of linked sections.
         MOV [%OrdPtr],ESI
    .10: MOV ESI,[%OrdPtr]
         CMP ESI,[%OrdTop]
         JNB .90:                                    ; End when there are no more sections.
         MOV EDI,[ESI]                               ; EDI is the linked section, EBX is its segment.
         ADDD [%OrdPtr],4                            ; Prepare for the next section.
         JNSt [EBX+SSS.Status],sssNotBSS,.50:        ; When the segment is uninitialized.
         ; Segment is initialized. Its final .Top will be determined by emitted size.
         BufferRetrieve [EBX+SSS.EmitBuffer]
         MOV EAX,ECX
         XOR EDX,EDX                                 ; EDX:EAX is the top of initialized segment, i.e. its emitted and virtual size.
         MOV [EBX+SSS.TopLow],EAX
         MOV [EBX+SSS.TopHigh],EDX
    .20: CMP EDI,EBX                                 ; Do nothing else with the 1st section, which is identical with segment EBX.
         JE .10:                                     ; Link the next section.
         Invoke ExpAlign::,EAX,[EDI+SSS.Alignment],0 ; Align the top of segment EBX by section's own alignment. Return stuff size in ECX.
         JECXZ .30:                                  ; If no alignment is necessary.
         ADD EAX,ECX
         ADC EDX,0                                   ; EDX:EAX is the new aligned top of segment EBX and bottom of section EDI.
         MOV [EBX+SSS.TopLow],EAX
         MOV [EBX+SSS.TopHigh],EDX
    .30: MOV [EDI+SSS.BottomLow],EAX
         MOV [EDI+SSS.BottomHigh],EDX
         MOV ESI,0x90                                ; Prepare alignment stuff in SIL.
         JSt [EBX+SSS.Purpose],sssPurposeCODE,.32:
         XOR ESI,ESI
    .32: BufferResize [EBX+SSS.EmitBuffer],ECX,Stuff=ESI ; Align the emitted contents of segment EBX.
         BufferRetrieve [EDI+SSS.EmitBuffer]
         BufferStore [EBX+SSS.EmitBuffer],ESI,ECX    ; Copy the emitted contents of section EDI to segment EBX.
         BufferRetrieve [EBX+SSS.EmitBuffer]         ; Reread the concatenated contents.
         MOV [EBX+SSS.TopLow],ECX
         MOV [EDI+SSS.TopLow],ECX
         BufferRetrieve [EDI+SSS.RelocBuffer]
         BufferStore [EBX+SSS.RelocBuffer],ESI,ECX   ; Copy unchanged relocations from section EDI to the base segment EBX.
         JMP .10:                                    ; The next section.

    .50: ; Segment is uninitialized. Its size is calculated from virtual sizes of its sections.
         MOV EAX,[EBX+SSS.TopLow]                    ; Bottom of segment EBX is always 0.
         MOV EDX,[EBX+SSS.TopHigh]                   ; EDX:EAX is top of uninitialized base segment, i.e. its virtual size.
         CMP EDI,EBX                                 ; Do nothing else with the 1st section, which is identical with segment EBX.
         JE .10:
         Invoke ExpAlign::,EAX,[EDI+SSS.Alignment],0 ; Align the top of segment EBX by section's own alignment. Return stuff size in ECX.
         JECXZ .60:                                  ; If no alignment is necessary.
         ADD EAX,ECX
         ADC EDX,0                                   ; EDX:EAX is the new aligned top of segment EBX and bottom of section EDI.
         MOV [EBX+SSS.TopLow],EAX
         MOV [EBX+SSS.TopHigh],EDX
    .60: MOV [EDI+SSS.BottomLow],EAX
         MOV [EDI+SSS.BottomHigh],EDX
         ADD [EDI+SSS.TopLow],EAX
         ADC [EDI+SSS.TopHigh],EDX
         MOV EAX,[EDI+SSS.TopLow]
         MOV EDX,[EDI+SSS.TopHigh]
         MOV [EBX+SSS.TopLow],EAX
         MOV [EBX+SSS.TopHigh],EDX
         JMP .10:                                    ; Link the next section.
    .90:EndProcedure SssLinkSections

↑ SssResolve BasePgm
  When segments and symbols from linked modules have been combined into BasePgm, SssResolve resolves old references `SSS.SegmPtr, SSS.GroupPtr, SSS.SymPtr` and replaces them with pointers to the corresponding objects in BasePgm.
Input
  BasePgm ^PGM is the combined program.
Output
  -
Errors
  -
Invoked by
  PgmCombine

    SssResolve Procedure BasePgm
        MOV EBX,[%BasePgm]
        ListGetFirst [EBX+PGM.SssList]
        JZ .90:
    .10:JNSt [EAX+SSS.Status],sssSegment|sssGroup|sssExtern,.80:
        MOV ECX,[EAX+SSS.SegmPtr]
        JECXZ .30:
        MOV ESI,[ECX+SSS.SegmPtr]
        MOV [EAX+SSS.SegmPtr],ESI
    .30:MOV ECX,[EAX+SSS.GroupPtr]
        JECXZ .50:
        MOV ESI,[ECX+SSS.GroupPtr]
        MOV [EAX+SSS.GroupPtr],ESI
    .50:MOV EDX,[EAX+SSS.SymPtr]
        TEST EDX
        JZ .80:
        MOV ECX,[EDX+SYM.SymbPtr]
        JECXZ .80:
        MOV [EAX+SSS.SymPtr],ECX
    .80:ListGetNext EAX
        JNZ .10:
    .90:EndProcedure SssResolve

↑ SssCreateImplicit Program

  SssCreateImplicit creates implicit groups and segments when a new program is initialized.

  Implicit common group `[COMGROUP]` is created in TINY model (default in formats BIN, BOOT, COM), it assumes `CS=DS=SS=PARA# [COMGROUP]`.

  No groups are created in FLAT memory model, segment registers are established by the program loader.

  Implicit segment names are `[.text], [.rodata], [.data], [.bss]` when `PROGRAM MODEL=FLAT` , and `[CODE], [RODATA], [DATA], [BSS], [STACK]` otherwise.

  Pseudosegment [PSP] is created in program formats COM and BOOT.

  Implicit groups `[CGROUP], [DGROUP], [SGROUP]` are created in near memory models and the segment registers is assumed to contain paragraph address of the group: `CS=PARA# [CGROUP], DS=PARA# [DGROUP], SS=PARA# [SGROUP]`. Usually there is just one code segment in near monocode MZ program (`MODEL=TINY | SMALL | COMPACT`), and if more code segments with different names are combined (from linked modules, for instance), they will be grouped here into an implicit code group `[CGROUP]` which should be used as their addressing frame.

  Similary, in near monodata MZ program (`MODEL=TINY | SMALL | MEDIUM` ) all data, read-only data and uninitialized data segments are grouped in [DGROUP] and segment register DS is assumed loaded with `DS=PARA# [DGROUP]`.

Implicit groups and segments
  class="PRE"> MODEL=TINY MODEL=SMALL MODEL=MEDIUM MODEL=COMPACT MODEL=LARGE|HUGE MODEL=FLAT [COMGROUP] [CGROUP] [CODE]* [CGROUP] [CODE]* [.text]* [CODE] [CODE] [DGROUP] [CODE] [RODATA]* [.rodata]* [RODATA] [DGROUP] [RODATA] [RODATA]* [DATA]* [.data]* [DATA] [RODATA] [DATA] [DATA]* [BSS]* [.bss]* [BSS] [DATA] [BSS] [BSS]* [SGROUP] [STACK] [BSS] [SGROUP] [SGROUP] [STACK] [SGROUP] [STACK] [STACK] [STACK] Segments marked with * are not members of a group.
Input
  Program Pointer to an empty PGM whose implicit segments and groups are created.
Output
  `PGM.SssList` is populated with implicit groups and segments.
Error
  -
Manual
  GROUP
  PROGRAM MODEL=
Invoked by
  PseudoPROGRAM
Invokes
  SssCreateGroup SssCreateSe SymCreateSe

    SssCreateImplicit Procedure Program
    CGROUP   LocalVar                                ; ^SSS [CGROUP] or 0.
    DGROUP   LocalVar                                ; ^SSS [DGROUP] or 0.
    SGROUP   LocalVar                                ; ^SSS [SGROUP] or 0.
    PSPsize  LocalVar                                ; SIZE# [PSP] or 0.
    SssStm   LocalVar Size=SIZE#STM                  ; Temporary STM required by SssCreateSe.
             ClearLocalVar
          ; Prepare temporary statement EBX.
          LEA EBX,[%SssStm]
          MOV EDI,[%Program]
          MOV [EBX+STM.Program],EDI
          MOV EAX,[EDI+PGM.LinePtr]
          MOV [EBX+STM.LinePtr],EAX
          ; Create pseudosegment [PSP] in formats COM and BOOT.
          MOV EAX,[EDI+PGM.Pgmopt.Status]
          AND EAX,pgmoptFormatMask
          Dispatch AL,pgmoptCOM,pgmoptBOOT
          JMP .20:
    .pgmoptCOM:
          MOVD [%PSPsize],0x0100
          JMPS .20:
    .pgmoptBOOT:
          MOVD [%PSPsize],0x7C00

    .20:  ; Create implicit groups according to model.
          MOV EAX,[EDI+PGM.Pgmopt.Status]
          MOV ESI,pgmoptWidthMask                    ; Identical with sssWidthMask.
          MOV ECX,EAX
          AND ESI,EAX                                ; Default segment width is adopted from program width.
          OR ESI,sssGroup+sssImplicit+sssPublic      ; Status of implicit groups.
          AND EAX,pgmoptModelMask
          Dispatch EAX,pgmoptFLAT,pgmoptTINY,pgmoptSMALL,pgmoptMEDIUM,pgmoptCOMPACT,pgmoptLARGE,pgmoptHUGE
    .pgmoptTINY:
          Invoke SssCreateGroup,EBX,=B"COMGROUP",8,ESI,sssPurposeCODE+sssPurposeDATA+sssPurposeRODATA+sssPurposeBSS+sssPurposeSTACK
          Invoke SymCreateSe::,EAX,[EBX+STM.Program]
          MOV [%CGROUP],EAX
          MOV [%DGROUP],EAX
          MOV [%SGROUP],EAX
          CALL .CreatePSP:
          JMP .pgmoptREAL:

    .pgmoptCOMPACT:
    .pgmoptSMALL:
          Invoke SssCreateGroup,EBX,=B"CGROUP",6,ESI,sssPurposeCODE
          Invoke SymCreateSe::,EAX,[EBX+STM.Program]
          MOV [%CGROUP],EAX
          CALL .CreatePSP:
    .40:  JSt [EDI+PGM.Pgmopt.Status],pgmoptCOMPACT,.pgmoptLARGE:
    .pgmoptMEDIUM:
          Invoke SssCreateGroup,EBX,=B"DGROUP",6,ESI,sssPurposeDATA+sssPurposeRODATA+sssPurposeBSS
          Invoke SymCreateSe::,EAX,[EBX+STM.Program]
          MOV [%DGROUP],EAX
    .pgmoptLARGE:
    .pgmoptHUGE:
          Invoke SssCreateGroup,EBX,=B"SGROUP",6,ESI,sssPurposeSTACK
          Invoke SymCreateSe::,EAX,[EBX+STM.Program]
          MOV [%SGROUP],EAX
    .pgmoptREAL:                                     ; Real-mode programs use implicit segments [CODE],[DATA] etc.
          RstSt ESI,sssGroup
          SetSt ESI,sssSegment+sssSection            ; Prepare status of implicit segments.
          Invoke SssCreateSe::,EBX,0,=B"CODE",4,ESI,sssPurposeCODE,16
          MOV EDX,[%CGROUP]
          MOV [EAX+SSS.GroupPtr],EDX
          Invoke SssCreateSe::,EBX,0,=B"RODATA",6,ESI,sssPurposeRODATA,16
          MOV EDX,[%DGROUP]
          MOV [EAX+SSS.GroupPtr],EDX
          Invoke SssCreateSe::,EBX,0,=B"DATA",4,ESI,sssPurposeDATA,16
          MOV [EAX+SSS.GroupPtr],EDX
          Invoke SssCreateSe::,EBX,0,=B"BSS",3,ESI,sssPurposeBSS,16
          MOV [EAX+SSS.GroupPtr],EDX
          Invoke SssCreateSe::,EBX,0,=B"STACK",5,ESI,sssPurposeSTACK,16
          MOV EDX,[%SGROUP]
          MOV [EAX+SSS.GroupPtr],EDX
          JMP .90:

    .CreatePSP: PROC ; Create segment PSP in group EAX with size [%PSPsize]. EBX=^PGM, EAX=^SSS group. Output:EDX=^SSS group, [%PSPsize]-0.
          MOV EDX,EAX
          MOV ECX,[%PSPsize]
          JECXZ .P9:
          Invoke SssCreateSe,EBX,0,=B'PSP',3,sssImagePrefix+sssSegment+sssImplicit+sssUsed+sssDefinedInPass,sssPurposePSP,0
          MOV [EAX+SSS.TopLow],ECX
          MOV [EAX+SSS.GroupPtr],EDX
          BufferStoreDword [EDX+SSS.SssOrdBuffer],EAX ; Add segment [PSP] to the group EDX.
          MOVD [%PSPsize],0
    .P9:  RET
          ENDP  .CreatePSP:

    .pgmoptFLAT:
         RstSt ESI,sssGroup
         SetSt ESI,sssSegment+sssSection            ; Prepare status of implicit segments.
         Invoke SssCreateSe::,EBX,0,=B".text",5,ESI,sssPurposeCODE,16
         Invoke SssCreateSe::,EBX,0,=B".rodata",7,ESI,sssPurposeRODATA,16
         Invoke SssCreateSe::,EBX,0,=B".data",5,ESI,sssPurposeDATA,16
         Invoke SssCreateSe::,EBX,0,=B".bss",4,ESI,sssPurposeBSS,16
    .90:EndProcedure SssCreateImplicit

↑ SssEmit Sss, EmitBuffer, RelocBuffer, AlignBytes
  SssEmit stores alignment and data from Stm.EmitBuffer to Sss.EmitBuffer. Then it stores and patches relocations from Stm.RelocBuffer to Sss.RelocBuffer.
  It is invoked from StmFlush after each statement has been executed.
Input
  Sss Pointer to section where data will be emitted. Bottom of this unlinked section is 0.
  EmitBuffer Pointer to BUFFER with code emitted by the current statement. The buffer 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.
  AlignBytes Plain signed integer number of alignment stuff bytes which should add to `Sss.Org` before storing the data from %EmitBuffer to Sss.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`, too. AlignBytes can also be negative in statements like `$ EQU $-8`.
Output
  Section properties are updated.
Error
  Errors are reported with macro Msg.
Invoked by
  StmFlush SymCreateLiteral

    SssEmit Procedure Sss, EmitBuffer, RelocBuffer, AlignBytes
    OldOrg   LocalVar                                ; Origin before alignment and emitting.
        MOV EDI,[%Sss]
        MOV EAX,[%AlignBytes]
        CDQ
        MOV ECX,[EDI+SSS.OrgLow]
        MOV [%OldOrg],ECX
        ADD EAX,ECX
        MOV EBX,EAX
        ADC EDX,[EDI+SSS.OrgHigh]                    ; EBX and EDX:EAX is aligned origin between 0 and 4G.
        JZ .10:
    .E6555:Msg '6555'                                ; Offset out of section limits.
        JMP .90:
    .10:BufferRetrieve [%EmitBuffer]                 ; Get the emitted size of current statement.
        ADD EAX,ECX
        ADC EDX,0
        MOV [EDI+SSS.OrgLow],EAX
        MOV [EDI+SSS.OrgHigh],EDX                    ; Establish new origin.
        CMP EDX,[EDI+SSS.TopHigh]
        JA  .12:
        CMP EAX,[EDI+SSS.TopLow]
        JBE .15
    .12:MOV [EDI+SSS.TopLow],EAX
        MOV [EDI+SSS.TopHigh],EDX
    .15:BufferRetrieve [EDI+SSS.EmitBuffer]
        MOV EDX,ECX                                  ; ECX and EDX is the old emited contents size.
        SUB EAX,ECX
        JNG .20:                                     ; EAX is the required SSS.EmitBuffer enlargement, if positive.
        BufferNew [EDI+SSS.EmitBuffer],EAX,Zeroed=No ; Enlarge the size of SSS.EmitBuffer claimed contents.
    .20:BufferRetrieve [EDI+SSS.EmitBuffer]          ; ESI points to the bottom of of (enlarged) contents in SSS.EmitBuffer.
        MOV ECX,EBX                                  ; EBX is aligned origin.
        SUB ECX,EDX                                  ; EDX is previous size of SSS.EmitBuffer claimed contents.
        JNA .40:                                     ; Skip when no alignment stuff is required.
        XOR EAX,EAX
        JNSt [EDI+SSS.Purpose],sssPurposeCODE,.30:
        MOV AL,0x90                                  ; The stuff is either 0 or NOP.
    .30:MOV EDI,ESI                                  ; SSS.EmitBuffer bottom.
        ADD EDI,[%OldOrg]                            ; ECX bytes of alignment stuff AL will be emitted at EDI.
        REP STOSB                                    ; Emit the alignment stuff.
    .40:MOV EDI,ESI                                  ; SSS.EmitBuffer bottom.
        MOV EDX,ESI
        ADD EDI,EBX
        BufferRetrieve [%EmitBuffer]
        REP MOVSB                                    ; Emit the instruction|data.
        BufferRetrieve [%RelocBuffer]                ; Now store relocations, if there are any.
        JECXZ .90:                                   ; If %RelocBuffer is unspecified or empty.
        MOV EDI,[%Sss]
    .60:ADD [ESI+RELOC.OrgLow],EBX                   ; Patch the relocation by EBX=temporary SSS.Org.
        MOV [ESI+RELOC.Section],EDI
        BufferStore [EDI+SSS.RelocBuffer],ESI,SIZE#RELOC
        ADD ESI,SIZE# RELOC
        SUB ECX,SIZE#RELOC
        JA .60:                                      ; The next relocation (there may be two in one statement).
    .90:EndProcedure SssEmit

↑ SssRelocResolve Segment, Program
  SssRelocResolve will resolve all relocations in the Segment.
  It is invoked when an executable program is combined and linked, external symbols resolved and virtual segment addresses fixed in image.
Input
  Segment = ^SSS.
  Program is pointer to PGM.
Output
  Data or code in SSS.EmitBuffer is modified: emited relocable Word/Dword/Fword pointed to by `RELOC.Org` is fixed up according to relocation type.
  Resolved relocations in `Segment.RelocBuffer` are then marked as relocResolved.
Error
  -
Invoked by
  PfelfsoCompile PgmRelocResolve
Invokes
  RelocResolve

    SssRelocResolve Procedure Segment, Program
    ;;EmittedBottom      LocalVar ; Pointer to the bottom of emitted data (withing the contents of SSS.EmitBuffer).
    ;;EmittedPtr         LocalVar ; Pointer to the relocated word|dword in emitted data. Always between %EmittedBottom and %EmittedTop.
    ;;EmittedTop         LocalVar ; Pointer to the top of emitted data.
    ;ůRelocBottom        LocalVar ; Pointer to the first RELOC record.
    ;RelocTop           LocalVar ; Pointer to the top of array of RELOC records.
    ;Pgmopt             LocalVar ; Program format and model (cache copy of Program.Pgmopt.Status).
         MOV EBX,[%Program]
         MOV EAX,[%Segment]
         BufferRetrieve [EAX+SSS.RelocBuffer]
         ADD ECX,ESI
    .20: CMP ESI,ECX                                 ; Walk through relocations in the loop .20: .. .Skip:.
         JNB .90:
         Invoke RelocResolve::,ESI,EBX
         ADD ESI,SIZE# RELOC
         JMP .20:                                    ; The next RELOC record.
    .90:EndProcedure SssRelocResolve

↑ SssUpdateByEmit SssObject
  SssUpdateByEmit will update SSS.TopFA and SSS.Top by the size of SSS.EmitBuffer.
  It is invoked when EmitBuffer was changed.
Input
  SssObject = ^SSS.
Output
  VA and FA is updated.
Error
  -
Invoked by
  PfelfConvertSymbols PfelfRelaConvert PfelfsoDynamic PfelfsoHashTable PfelfxPltGot

    SssUpdateByEmit Procedure SssObject
         MOV EDI,[%SssObject]
         TEST EDI
         JZ .90:
         BufferRetrieve [EDI+SSS.EmitBuffer]
         MOV EAX,[EDI+SSS.BottomLow]
         MOV EDX,[EDI+SSS.BottomHigh]
         ADD EAX,ECX
         ADC EDX,0
         MOV [EDI+SSS.TopLow],EAX
         MOV [EDI+SSS.TopHigh],EDX
         ADD ECX,[EDI+SSS.BottomFA]
         MOV [EDI+SSS.TopFA],ECX
    .90:EndProcedure SssUpdateByEmit

     ENDPROGRAM sss

▲Back to the top▲