EuroAssembler Index Manual Download Source Macros


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.
INCLUDEHEAD  \  ; Include headers of another modules used in this module.
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,     \
syswin.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
sssStructure000
sssSectionsection VA0 or ^explicit group^segment
sssSegment00 or ^explicit group^self
sssGroup0^self0 or ^base segment
sssExtern00^extern
SSS pointers at link time
Object type.Bottom.GroupPtr.SegmPtr
sssStructure000
sssSectionN/AN/AN/A
sssSegmentsegment VA^explicit or implicit group^self
sssGroupgroup VA^self^base segment
sssExternextern VA^explicit or implicit group^extern
SSS STRUC         ; +00h.
.NamePtr      D D ; 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 sssExternsssGroupsssSegment
+sssPublic
sssSegment
+sssStack
sssSegment
+sssCommon
sssSegment
+sssPrivate
sssExternCOMMONPRIVATEPRIVATEPRIVATEPRIVATEPRIVATE
sssGroupPRIVATECOMMONPRIVATEPRIVATEPRIVATEPRIVATE
sssSegment+sssPublicPRIVATEPRIVATEPUBLICPUBLICPUBLICPRIVATE
sssSegment+sssStackPRIVATEPRIVATEPUBLICPUBLICPUBLICPRIVATE
sssSegment+sssCommonPRIVATEPRIVATEPUBLICPUBLICCOMMONPRIVATE
sssSegment+sssPrivatePRIVATEPRIVATEPRIVATEPRIVATEPRIVATEPRIVATE
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.
  1. Base section with identical name
  2. Nonliteral code sections in the order as they were declared
  3. Literal code sections in alphabetical order ([@RT0],[$RT1],[@RT2]...)
  4. Nonliteral read-only data sections in the order as they were declared
  5. Literal read-only data sections in the decreasing alignment order ([@LT64],[@LT32],,,[@LT1])
  6. Nonliteral data sections in the order as they were declared
  7. Literal data sections in the decreasing alignment order ([@LT64],[@LT32],,,[@LT1])
  8. All other nonBSS sections in the order as they were declared
  9. BSS sections in the order as they were declared
  10. 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
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▲