EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

pgm.htm
Class
PGM
Encodings
PgmEnc
Procedures
PgmCombine
PgmCreateImportModule
PgmCreateProgram
PgmDestroy
PgmDetectImportModule
PgmEvalEntry
PgmGetCurrent
PgmGetEntryAddress
PgmGlobalEntry
PgmGroupByModel
PgmCheckDirty
PgmLink
PgmLinkSections
PgmListGlobals
PgmListLiterals
PgmListMap
PgmOrderSegments
PgmOrderSymbols
PgmParameters
PgmRelocResolve
PgmResizeGroups
PgmSelectModules
PgmStoreGlobalSymbols
PgmStreamImage
PgmSymResolve

Pgm represents a program defined with source text between PROGRAM/ENDPROGRAM statements. Product of Pgm assembly is binary/object/executable file.

In other assemblers it is often referred as module or assembly unit . EuroAssembler accepts more than one program in a source file.

Pgm has its own memory pool.


    EUROASM NOWARN=2101
pgm 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.
syswin.htm,  \
chunk.htm,   \
ctx.htm,     \
dict.htm,    \
ea.htm,      \
eaopt.htm,   \
exp.htm,     \
lst.htm,     \
msg.htm,     \
pass.htm,    \
pf.htm,      \
pgmopt.htm,  \
reloc.htm,   \
src.htm,     \
sss.htm,     \
stm.htm,     \
sym.htm,     \
var.htm,     \
;;
pgm HEAD ; Start of module interface.
↑ PGM
Object of this class is allocated in Src.Pool.
Program processing object model
PseudoPROGRAM ; Parse pseudoinstruction PROGRAM. CtxCreate PgmCreateProgram ┌─> PgmParameters ; Reassemble operands of PROGRAM statement. PgmoptAssemble PgmoptSetDefaults SssCreateImplicit PassCreate PseudoENDPROGRAM ; Parse pseudoinstruction ENDPROGRAM. PgmCheckDirty ; Detect if something was emitted in this program. PgmLinkSections ; Link virtual adress of each section SssLinkSections PassInspect >──final─┐ ; Check if the final pass just ended. PassDestroy└─<redirect assembly back │ ; Only if more passes are required. VarListMerge <───────┘ ; Copy %variables and macrodefinitions MacListMerge ; from the ending program to its parent, if it exists. PgmListLiterals ; Append info to the listing. PassDestroy PgmDestroy ChunkTotalLines ; Write info to output. PfOutput ; See Linker object model for more detailed info. PgmLinkSections ; Link emitted code + relocations of each section SssLinkSections PgmListMap ; Append info to the listing. PgmListGlobals ; Append info to the listing. CtxDiscard CtxDestroy continue below ENDPROGRAM statement
PGM STRUC
.NamePtr            D DWORD ; Pointer to the program name.
.NameSize           D DWORD ; Size of the program name.
.Pool               D DWORD ; Program pool of dynamic memory.    
.Status             D DWORD ; Program Boolean flags in PgmEncoding.
                        ; +10h.
.PassNr             D DWORD ; Pass number.
.CurrentSect        D DWORD ; ^SSS with .Status:sssSection - a section which Pgm emits to.
.CurrentStm         D DWORD ; ^STM with the last parsed statement.
.LinePtr            D DWORD ; Pointer to memory-mapped physical line with current statement of this PROGRAM.
                        ; +20h.
.Errorlevel         D DWORD ; Errorlevel of this program.
.PassPtr            D DWORD ; Pointer to PASS or 0 when no pass is initialized.
.NrOfInitSegments   D DWORD ; How many groups+segments in image are initialized (trailing BSS and STACK do not count).
.ModulePgmList      D DWORD ; ^LIST of PGM modules loaded from linked/imported files.
                        ; +30h.
.SssList            D DWORD ; ^LIST with groups, segments, sections, externs and structures (SSS objects).                        
.SymList            D DWORD ; ^LIST with program symbols (SYM objects).
.SegOrdBuffer       D DWORD ; ^BUFFER with DWORD pointers to segments (^SSS) in image order.
.SymOrdBuffer       D DWORD ; ^BUFFER with DWORD pointers to symbols (^SYM) in image order.
                        ; +40h.
; Synchronous tables describing files included in the program.
.InclFilesNr        D DWORD ; Number of DWORDS in .InclFilesTable,.InclLinePtrTable,.InclFileTimeTable.
.InclFilesTable     D DWORD ; Pointer to the table of DWORDs, each contains pointer to FILE structure.
.InclLinePtrTable   D DWORD ; Pointer to the table of DWORDs, each contains LinePtr to INCLUDE* statement.
.InclFileTimeTable  D DWORD ; Pointer to the table of DWORDs, each contains DosDateTime of included file.
                        ; +50h.
; Synchronous tables describing files linked in the program.
.MaxLinks           D DWORD ; Copy of EUROASM MAXLINKS= option which was in charge on PgmCreateProgram.
.LinkFilesNr        D DWORD ; Number of DWORDS in .LinkFileNamesTable and .LinkLinePtrTable.
.LinkFileNamesTable D DWORD ; Pointer to the table of DWORDs, each contains pointer to ASCIIZ filename.
.LinkLinePtrTable   D DWORD ; Pointer to the table of DWORDs, each contains LinePtr to LINK statement.
                        ; +60h.
.DSONameBuffer      D DWORD ; ^BUFFER with DWORD pointer+size of the name of dynamically loaded DSO module without path. Empty if it isn't ELFSO format.
.EntryExp           DS EXP    ; Program entry evaluated to EXP structure by PgmEvalEntry.
.Pgmopt             DS PGMOPT ; Program options of this program as PGMOPT object.
.Eaopt              DS EAOPT  ; EUROASM options valid at the start of this program as EAOPT object.
 ENDSTRUC PGM
↑ PgmEnc
Encoding of flags used in PGM.Status. More program-option flags are defined in PGM.Pgmopt.Status.
; Assembly flags.
pgmEnvelope      = 0x0000_0001 ; Artificial PROGRAM block encapsulating all source file statements.
pgmEnvelDirty    = 0x0000_0002 ; Something was emitted to envelope program, do list its PROGRAM/ENDPROGRAM statements.
;                = 0x0000_0004
pgmNoMsg         = 0x0000_0008 ; Used to suppress Msg during ChunkSubHead detection.
pgmLastPass      = 0x0000_0010 ; Program is in the final pass.
pgmLastJustSet   = 0x0000_0020 ; Set together with pgmLastPass, reset in SrcAssemble when used.
pgmPassInit      = 0x0000_0040 ; Pass was already initialized with 256/0x7C00 dummy bytes in COM/BOOT format.
pgmFixingPass    = 0x0000_0080 ; Pass number last but one (MAXPASSES-1).
; Linker flags.
pgmLinking       = 0x0000_0100 ; Program was finally assembled, now it is linked.
pgmLoadsELFSO    = 0x0000_0800 ; This base program loads at least one ELFSO library.
pgmFormatELF     = 0x0000_1000 ; Program format=ELF.
pgmFormatELFX    = 0x0000_2000 ; Program format=ELFX.
pgmFormatELFSO   = 0x0000_4000 ; Program format=ELFSO.
pgmEntryReported = 0x0000_8000 ; E7710 or E7711 was already reported, see PgmGetEntryAddress.
pgmSelected      = 0x0001_0000 ; Module is referenced, it will be combined and linked to base program.
pgmImpLibMember  = 0x0002_0000 ; Module is a pure import library member.
pgmIsModule      = 0x0004_0000 ; This program is OMF library module. It requires 512 alignment.
pgmRealMode      = 0x0008_0000 ; Program format uses by default real-mode segment names [CODE]... rather than prot-mode names [.text]...

  ENDHEAD pgm
[.text]
↑ PgmCreateProgram StmPtr
PgmCreateProgram allocates PGM object on Src.Pool, initializes program structures, creates implicit groups, segments, sections. Context ctxPROGRAM with delocalized program name must be already pushed on context stack.
Input
StmPtr is pointer to parsed PROGRAM statement.
Output
EAX Pointer to just created PGM object.
Invokes
CtxPeek
Invoked by
PseudoPROGRAM
PgmCreateProgram Procedure StmPtr
     MOV EBX,[%StmPtr]
     PoolNew [Src.Pool::],SIZE# PGM
     MOV EDI,EAX ; ^PGM.
     MOV [%ReturnEAX],EAX
     ; Initialize program object members.
     Clear EDI,Size=SIZE#PGM
     MOV EAX,[EBX+STM.ChunkPtr]
     MOV ECX,'0470'                              ; Assembling program "!1S".
     JNSt [EAX+CHUNK.Status],chunkEnvelope,.10:
     MOV ECX,'0270'                              ; Assembling source "!1S".
     SetSt [EDI+PGM.Status],pgmEnvelope
 .10:Invoke CtxPeek::,ctxPROGRAM,0
     MsgUnexpected cc=C
     Msg ECX,EAX                                 ; Context Assembling source/program "!1S".
     MOV ESI,[EAX+CTX.NamePtr]
     MOV ECX,[EAX+CTX.NameSize]
     MOV [EDI+PGM.NamePtr],ESI
     MOV [EDI+PGM.NameSize],ECX
     PoolCreate Size=%EaPoolSize, ErrorHandler=EaMallocError::
     MOV [EDI+PGM.Pool],EAX
     MOV EDX,EAX
     MOV [EDI+PGM.CurrentStm],EBX
     MOV EAX,[EBX+STM.LinePtr]
     MOV [EDI+PGM.LinePtr],EAX
     ListCreate EDX,SIZE#SYM
     MOV [EDI+PGM.SymList],EAX
     ListCreate EDX,SIZE#PGM
     MOV [EDI+PGM.ModulePgmList],EAX
     PoolNew EDX,SIZE#PASS
     MOV [EDI+PGM.PassPtr],EAX
     ListCreate EDX,SIZE#SSS
     MOV [EDI+PGM.SssList],EAX
     BufferCreate [EDI+PGM.Pool],Size=16*4       ; Initialize for estimated 16 segments in the program.
     MOV [EDI+PGM.SegOrdBuffer],EAX
     BufferCreate [EDI+PGM.Pool],Size=2048*4     ; Initialize for estimated 2048 symbols in the program.
     MOV [EDI+PGM.SymOrdBuffer],EAX
      ; Default segments on SssList will be created later, when PROGRAM keyword will have been assembled to Pgmopt.
     LEA ESI,[EDI+PGM.Eaopt]
     CopyTo ESI, Ea.Eaopt::, Size=SIZE#EAOPT
     LEA ESI,[EDI+PGM.Pgmopt]
     CopyTo ESI, Src.Pgmopt::, Size=SIZE#PGMOPT  ; Initialize Pgmopt from source Pgmopt.
     ; Allocate included files tables Pgm.InclFilesTable,Pgm.InclFileTimeTable,Pgm.InclLinePtrTable.
     MOV ECX,[Ea.Eaopt.MaxInclusions::]
     INC ECX
     SHL ECX,2
     PoolNew [EDI+PGM.Pool],ECX
     MOV [EDI+PGM.InclFilesTable],EAX
     PoolNew [EDI+PGM.Pool],ECX
     MOV [EDI+PGM.InclFileTimeTable],EAX
     PoolNew [EDI+PGM.Pool],ECX
     MOV [EDI+PGM.InclLinePtrTable],EAX
     ; Allocate linked files tables Pgm.LinkFileNamesTable, Pgm.LinkLinePtrTable.
     MOV ECX,[Ea.Eaopt.MaxLinks::]
     MOV [EDI+PGM.MaxLinks],ECX
     INC ECX
     SHL ECX,2
     PoolNew [EDI+PGM.Pool],ECX
     MOV [EDI+PGM.LinkFileNamesTable],EAX
     PoolNew [EDI+PGM.Pool],ECX
     MOV [EDI+PGM.LinkLinePtrTable],EAX
     ; Start with program pass number 1.
     INCD [EDI+PGM.PassNr]
    EndProcedure PgmCreateProgram
↑ PgmDestroy PgmPtr, StmPtr
PgmDestroy performs the final job when the final pass ended and is being destroyed: it will load other modules linked to this program by pseudoinstruction LINK ( PseudoLINK), link, compile, format and write the output file, using PfOutput.
The ending program is not removed from context stack yet.
Input
PgmPtr Pointer to PGM object.
StmPtr Pointer to STM object with ENDPROGRAM statement.
Output
CF=0
Error
CF=1 Errors are reported with macro Msg.
Invokes
ChunkTotalLines PfOutput PgmCheckDirty PgmListGlobals PgmListMap
Invoked by
PseudoENDPROGRAM
PgmDestroy Procedure PgmPtr, StmPtr
      MOV EBX,[%PgmPtr]
      JNSt [EBX+PGM.Status],pgmEnvelope,.50:
      ; Curent program is source envelope, Pgm.Errorlevel will be combined with Src.Errorlevel.
      MOV EAX,[Src.Errorlevel::]
      CMP EAX,[EBX+PGM.Errorlevel]
      JNA .30:
      MOV [EBX+PGM.Errorlevel],EAX
 .30: Invoke ChunkTotalLines::         ; Count lines in source+includes.
      MOV EDX,EAX
      Invoke PgmCheckDirty,EBX         ; Returns CF=1 if something was emitted or linked to the envelope program.
      SBB EDI,EDI                      ; EDI is now -1 if program dirty, 0 if empty.
      JZ .40:                          ; If ending envelope program is empty, output file is not generated.
      Invoke PfOutput::,EBX            ; Format and write envelope program to disk, possibly overwriting embedded program with the same name.
 .40: Msg '0750',EBX,EDX,[EBX+PGM.PassNr],[EBX+PGM.Errorlevel] ; Source program "!1S" (!2D lines) assembled in !3D passes with errorlevel !4D.
      TEST EDI
      JZ .80:                          ; Empty envelope program does not list maps and globals.
      JMPS .60:
 .50: ; Program EBX is not an envelope, i.e. it was explicitly specified as PROGRAM/ENDPROGRAM block.
      Invoke PfOutput::,EBX            ; Generate output file (even when empty).
      Msg '0650',EBX,[EBX+PGM.PassNr],[EBX+PGM.Errorlevel] ; Program "!1S" assembled in !2D passes with errorlevel !3D.
 .60: ; Common continuation for both envelope and embedded program.
      JNSt [EBX+PGM.Pgmopt.Status],pgmoptLISTMAP,.70:
      Invoke PgmListMap,EBX,[%StmPtr]  ; List map of linked groups and segments.
 .70: JNSt [EBX+PGM.Pgmopt.Status],pgmoptLISTGLOBALS,.80:
      Invoke PgmListGlobals,EBX,[%StmPtr] ; List global symbols.
 .80: MOV EDX,[EBX+PGM.Pool]
      PoolDestroy EDX
      Msg cc=C,'2575','Pgm',EDX        ; Deallocation of virtual memory !1C.Pool !2Hh failed.
 .90:EndProcedure PgmDestroy
↑ PgmOrderSymbols Pgm

This procedure decides which symbols should be stored in the symbol table of COFF|ELF module. It will put those symbols in desired order, sort them alphabetically, make them unique and store pointers to those symbols into Pgm.SymOrdBuffer.

Symbols which go to the symbol table:

  1. Special unnamed symbol with index 0 (NUL symbol).
  2. Pseudosymbol .file specifying the source file name.
  3. symSe symbols representing the bottom of used (dirty) segments.
  4. Private (local) symbols. Literals are not stored.
  5. Global symbols. Resolved externals are not stored.

Symbols within each of those divisions are sorted alphabetically by name (FQN) and made unique.

SYM.NameIndex and SYM.NameDynIndex are updated.

Input
Pgm points to PGM object whose PGM.SymList specifies all program symbols.
Output
Pgm.SymOrdBuffer is filled with DWORD pointers to SYM symbols in the desired order.
Invoked by
PfcoffCompile PfelfCompile PfelfsoCompile PfelfxCompile
Invokes
EaBufferRelease EaBufferReserve EaBufferSort
PgmOrderSymbols Procedure Pgm
BufEnd    LocalVar                               ; Pointer to the end of buffer contents.
SymNulBuf LocalVar                               ; ^BUFFER for null and file symbols.
SymSegBuf LocalVar                               ; Segment symbols.
SymLocBuf LocalVar                               ; Local symbols.
SymGlbBuf LocalVar                               ; Global symbols.
    Invoke EaBufferReserve::,PgmOrderSymbols
    MOV [%SymNulBuf],EAX
    Invoke EaBufferReserve::,PgmOrderSymbols
    MOV [%SymSegBuf],EAX
    Invoke EaBufferReserve::,PgmOrderSymbols
    MOV [%SymLocBuf],EAX
    Invoke EaBufferReserve::,PgmOrderSymbols
    MOV [%SymGlbBuf],EAX
    MOV EBX,[%Pgm]
    MOV EDX,[EBX+PGM.SymOrdBuffer]
    TEST EDX
    JNZ .05:
    BufferCreate [EBX+PGM.Pool]
    MOV [EBX+PGM.SymOrdBuffer],EAX
.05:ListGetFirst [EBX+PGM.SymList]
    MOV EBX,[EBX+PGM.SymOrdBuffer]               ; EBX is now the output buffer.
    JZ .90:
.10:MOV EDX,[%SymNulBuf]
    CMPD [EAX+SYM.NameSize],0
    JE .70:                                      ; Store NUL and .file symbols.
    MOV EDX,[%SymSegBuf]
    JSt [EAX+SYM.Status],symSe,.70:              ; Store segment's symbol.
.30:MOV EDX,[%SymLocBuf]                         ; Symbol EAX is global or local.
    JNSt [EAX+SYM.Status],symScopeMask,.70:      ; Go to store local symbols.
    JSt [EAX+SYM.Status],symResolved,.80:        ; Omit already resolved extern symbols.
    MOV EDX,[%SymGlbBuf]                         ; Store public|extern|import|export symbol.
.70:BufferStoreDword EDX,EAX
.80:ListGetNext EAX                              ; The next program symbol.
    JNZ .10:

PgmOrderSymbols.Reorder: PROC1 ; Copy pointers from buffer EDX to buffer EBX, remove duplicates. Clobbers EAX,ECX,EDX,ESI,EDI.
    Invoke EaBufferSort::,EDX                     ; Sort symbols in buffer EDX by name alphabetically.
    BufferRetrieve EDX
    ADD ECX,ESI
    MOV [%BufEnd],ECX
    JECXZ .R9:
    MOV EDI,-1
    MOV EDX,EDI                                  ; EDI,EDX will be the memory of previous symbol name.
.R1:CMP ESI,[%BufEnd]
    JNB .R9:
    LODSD
    Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],EDI,EDX
    JE .R1:                                      ; Skip duplicated symbol.
    MOV EDI,[EAX+SYM.NamePtr]                    ; Prepare for the next Compare.
    MOV EDX,[EAX+SYM.NameSize]
    BufferStoreDword EBX,EAX
    JMP .R1:
.R9:RET
    ENDP1 PgmOrderSymbols.Reorder:

.90:MOV EDX,[%SymNulBuf]
    CALL .Reorder:
    MOV EDX,[%SymSegBuf]
    CALL .Reorder:
    MOV EDX,[%SymLocBuf]
    CALL .Reorder:
    BufferRetrieve EBX
    SHR ECX,2
    MOV [%ReturnECX],ECX
    MOV EDX,[%SymGlbBuf]
    CALL .Reorder:
    Invoke EaBufferRelease::,[%SymNulBuf]
    Invoke EaBufferRelease::,[%SymSegBuf]
    Invoke EaBufferRelease::,[%SymLocBuf]
    Invoke EaBufferRelease::,[%SymGlbBuf]
   EndProcedure PgmOrderSymbols
↑ PgmGetCurrent
PgmGetCurrent returns the currently assembled program object.
Input
-
Output
CF=0
EAX= pointer to PGM object.
Error
CF=1 if no ctxPGM found on context stack.
EAX=0
Invokes
CtxPeek
Invoked by
ExpEvalIdentifier MsgProc PseudoENDPROC1 SssFindByIndex SssFindByName StmParse SymCreate SymFindByIndex SymFindByInterName SymFindByName VarExpand
PgmGetCurrent Procedure
      SUB EAX,EAX
      MOV [%ReturnEAX],EAX
 .20: Invoke CtxPeek::, ctxPROGRAM,EAX
      JC .90:
      MOV ECX,[EAX+CTX.ObjPtr]
      JECXZ .20:
      MOV [%ReturnEAX],ECX
 .90:EndProcedure PgmGetCurrent
↑ PgmListLiterals Pgm
PgmListLiterals employs StmListing to dump all data and code literals defined in the program. They are listed at the end of program listing, just before ENDPROGAM statement.
PgmListLiterals is invoked from PseudoENDPROGRAM just before the program is destroyed, but only if LISTLITERALS=ENABLED.
Input
Pgm Pointer to PGM whose literals are being listed.
Output
goes to the listing stream.
Error
CF=1 Errors are reported with macro Msg.
See also
StmListing.
Invokes
EaBufferRelease EaBufferReserve SssFindByName StmClean StmCreate StmDestroy StmListing
Invoked by
PseudoENDPROGRAM
PgmListLiterals Procedure Pgm
LSFound  LocalVar                                ; Boolean flag. Nonzero if subproc .ListSection found the literal section.
SnBuffer LocalVar                                ; Temporary buffer to keep pointers to literals before sorting.
@RTnum   LocalVar                                ; Ordinal of runtime literal section (1,2,3..).
Stm      LocalVar Size=SIZE#STM                  ; Temprary fake statement used to feed StmListing.
SssName  LocalVar Size=8                         ; Room for the literal section name, such as "@LT4"
    LEA EDI,[%SssName]
    MOV EAX,"@LT#"
    STOSD
    XOR EAX,EAX
    STOSD
    MOV [%@RTnum],EAX
    Invoke EaBufferReserve::,%^PROC
    MOV [%SnBuffer],EAX
    PUSHD [Src.Lst.Status::]                     ; Save the global listing status during PgmListLiteral's performance.
      LEA EBX,[%Stm]
      Invoke StmCreate::,EBX
      ; List data literals.
      MOV EAX,"LT64"
      CALL .ListSection:
      MOV EAX,"LT32"
      CALL .ListSection:
      MOV EAX,"LT16"
      CALL .ListSection:
      MOV EAX,"LT8"
      CALL .ListSection:
      MOV EAX,"LT4"
      CALL .ListSection:
      MOV EAX,"LT2"
      CALL .ListSection:
      MOV EAX,"LT1"
      CALL .ListSection:
      ; List code literals.
      MOV EAX,"RT0"
      CALL .ListSection:
      MOVD [%SssName],"@RT1"
.50:  MOV EAX,[%@RTnum]
      INC EAX
      MOV [%@RTnum],EAX
      LEA EDI,[%SssName+3]
      StoD EDI,Size=2,Signed=no
      CMP AL,9
      MOV EAX,[%SssName+1]
      JA .60:
      AND EAX,0x00FF_FFFF
.60:  CALL .ListSection:
      CMPD [%LSFound],0
      JNZ .50:                                   ; If @RT section was found, try the next one.
      Invoke StmDestroy::,EBX                    ; Otherwise all code-literals were listed.
    POPD [Src.Lst.Status::]
    Invoke EaBufferRelease::,[%SnBuffer]

.ListSection: PROC1 ; List literals from one section.
     ; Input: EAX=section name without '@', e.g. "LT32" or "RT0".
     ;        EBX=Pointer to %Stm.
     ; Output:EBX,EBP unchanged, other register clobbered.
     MOVD [%LSFound],0
     LEA EDI,[%SssName]
     MOV ECX,4                                   ; Section name size.
     MOV [EDI+1],EAX
     TEST EAX,0xFF00_0000                        ; Test if section name has 4 or 5 characters.
     JZ .S1:
     INC ECX
 .S1:Invoke SssFindByName::,sssSection,0,EDI,ECX,0
     JC .S9:
     JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.S9:
     MOV EDI,EAX                                 ; A literal section was found.
     MOV [%LSFound],EAX
     BufferClear [%SnBuffer]
     MOV EDX,[%Pgm]
     MOV [EDX+PGM.CurrentSect],EDI
     ListGetFirst [EDX+PGM.SymList]              ; Find all literal symbols which belong to section EDI.
     JZ .S4:
 .S2:; EAX is now pointer to a symbol from program's symbol list.
     JNSt [EAX+SYM.Status],symLiteral,.S3:       ; Skip when it's not a literal symbol.
     CMP [EAX+SYM.Section],EDI
     JNE .S3:                                    ; Skip when the symbol EAX does not belong to the listed section EDI.
     BufferStoreDword [%SnBuffer],EAX
 .S3:ListGetNext EAX
     JNZ .S2:                                    ; The next symbol.
 .S4:BufferRetrieve [%SnBuffer]
     SAR ECX,2                                   ; ECX is now number of literal symbols.
     JZ .S9:
     ; Symbols whose pointers are now in array at ESI will be sorted by offset and then listed.
     ; Otherwise in recursive literals, such as =W =W value, the inner literal with higher offset
     ;     would be listed sooner, which looks weird.
     ShellSort ESI,ECX, 4, .ByOffset:
.ByOffset: PROC1                                 ; Callback sorting procedure.
     PUSH EBX                                    ; EBX,ECX are callee-save registers.
      MOV EBX,[ESI]
      MOV EDX,[EDI]
      MOV EAX,[EDX+SYM.OffsetHigh]
      CMP EAX,[EBX+SYM.OffsetHigh]
      JNE .B5:
      MOV EAX,[EDX+SYM.OffsetLow]
      CMP EAX,[EBX+SYM.OffsetLow]
 .B5: JNC .B9:                                   ; Skip when they are in (desired) ascending order.
      MOV [ESI],EDX                              ; Otherwise swap both elements and return CF=1.
      MOV [EDI],EBX
 .B9:POP EBX
     RET
     ENDP1 .ByOffset:
     ; ESI is now an sorted array of ECX pointers to symbols which will be dumped as literal.
 .S5:LODSD
     PUSH ECX,ESI
       MOV EDX,EAX                               ; EDX is now ^SYM.
       SetSt [Src.Lst.Status::],lstLiteral       ; Tell StmListing how to treat this object.
       RstSt [Src.Lst.Status::],lstNoData+lstTRUE+lstFALSE
       Invoke StmClean::,EBX
       MOV EAX,[%Pgm]
       MOV [EBX+STM.Program],EAX
       MOV [EAX+PGM.CurrentSect],EDI
       MOV [EBX+STM.Section],EDI
       BufferStore [EBX+STM.SrcBuffer],[EDX+SYM.NamePtr],[EDX+SYM.NameSize]
       BufferStore [EBX+STM.SrcBuffer],=W(0x0A0D),2 ; Make new line.
       MOV EAX,[EDX+SYM.Size]
       MOV [EBX+STM.Size],EAX
       MOV EAX,[EDX+SYM.OffsetLow]
       MOV EDX,[EDX+SYM.OffsetHigh]
       MOV [EBX+STM.OffsetLow],EAX
       MOV [EBX+STM.OffsetHigh],EDX
       MOV [EDI+SSS.OrgLow],EAX
       ; Prepare emitted data of this literal to STM.EmitBuffer.
       SUB EAX,[EDI+SSS.BottomLow]
       BufferRetrieve [EDI+SSS.EmitBuffer]
       ADD ESI,EAX
       MOV ECX,[EBX+STM.Size]
       BufferStore [EBX+STM.EmitBuffer],ESI,ECX
       ; Prepare relocations of this literal to STM.RelocBuffer.
       BufferRetrieve [EDI+SSS.RelocBuffer]
       JECXZ .S7:
 .S6:  BufferStore [EBX+STM.RelocBuffer],ESI,SIZE#RELOC
       ADD ESI,SIZE#RELOC
       SUB ECX,SIZE#RELOC
       JA .S6:                                   ; The next relocation.
 .S7:  Invoke StmListing::,EBX
 .S8:POP ESI,ECX
     DEC ECX
     JNZ .S5:                                    ; The next literal symbol.
 .S9:RET
     ENDP1 .ListSection:
   EndProcedure PgmListLiterals
↑ PgmParameters PgmPtr, StmPtr
PgmParameters updates PGMOPT members from keyword operands of PROGRAM pseudoinstruction.
Input
PgmPtr Program whose options will be updated.
StmPtr Statement PROGRAM with keywords.
Output
Program options are updated.
Error
CF=1 Errors are reported with macro Msg.
See also
PgmoptAssemble members.
Invokes
DictLookup PgmGlobalEntry PgmoptAssemble
Invoked by
PseudoPROGRAM
PgmParameters Procedure PgmPtr, StmPtr
    MOV EBX,[%StmPtr]
    BufferRetrieve [EBX+STM.KeyBuffer]
    LEA EDX,[ESI+ECX]
.40:CMP ESI,EDX
    JNB .90:                           ; If no more keywords.
    MOV ECX,ESI
    LODSD                              ; Pointer to keyword name, e.g. MODEL=.
    MOV EDI,EAX
    LODSD                              ; Keyword name size.
    Invoke DictLookup::, DictPgmopt::, EDI,EAX
    Msg cc=C,'7821',ECX,DictPgmopt::   ; Unknown PROGRAM keyword "!1S". Expected one of !2L.
    MOV ECX,EAX                        ; Pgmopt index.
    LODSD                              ; Pointer to keyword value, e.g. SMALL.
    MOV EDI,EAX
    LODSD                              ; Keyword value size.
    JC .40:                            ; Skip when the option was unknown {E7821).
    StripSpaces EDI,EAX
    MOV EBX,[%PgmPtr]
%ord %SETA 0
opt %FOR %PgmoptList
%ord %SETA %ord+1
     %IF "%opt" == "ENTRY"
       %EXITFOR opt
     %ENDIF
    %ENDFOR opt
    CMP ECX,%ord << 16 + varTypeSysPgmopt ; is the parameter ENTRY?                        >>
    JNE .70:
    Invoke PgmGlobalEntry,EBX,EDI,EAX
.70:LEA EBX,[EBX+PGM.Pgmopt]
    Invoke PgmoptAssemble::, EBX,ECX,EDI,EAX
    JMP .40:
.90:EndProcedure PgmParameters
↑ PgmGlobalEntry Program, EntryPtr, EntrySize
PgmGlobalEntry checks if the program parameter ENTRY= specifies an identifier and declares a global symbol from it.
Input
Program Pointer to PGM of the program.
EntryPtr is pointer to the first character of entry name.
EntrySize is number of characters in entry.
Output
Entry global symbol is declared, if possible.
Error
-
Invokes
ExpParseIdentifier SymCreate
Invoked by
PgmParameters
PgmGlobalEntry Procedure Program, EntryPtr, EntrySize
TempStm LocalVar Size=SIZE# STM
     ClearLocalVar
     LEA EDI,[%TempStm]
     MOV EBX,[%Program]
     MOV ESI,[%EntryPtr]
     MOV ECX,[%EntrySize]
     MOV [EDI+STM.Program],EBX
     LEA EDX,[ESI+ECX]
     Invoke ExpParseIdentifier::,ESI,EDX,expFullstop
     JC .90:
     Invoke SymCreate::,symReferenced+symGlobalRef+symEntry,ESI,EAX,EDI
.90: EndProcedure PgmGlobalEntry
↑ PgmListMap Pgm, StmPtr
PgmListMap displays map of combined (linked) program segments at the end of listing, just below ENDPROGRAM statement. It is invoked only when PROGRAM LISTMAP=ON from PgmDestroy after segments have been combined, ordered, linked and streamed by PfOutput.
PgmListMap uses message buffer of ENDPROGRAM statement, the map is printed in listing but not on stdout.
Input
Pgm is pointer to PGM - completely assembled, combined and linked program.
StmPtr is pointer to STM object with ENDPROGRAM statement.
Output
-
Error
-
See also
PgmListGlobals.
Invokes
DictSearchByData EaBufferRelease EaBufferReserve SssPurposeToText
Invoked by
PgmDestroy
PgmListMap Procedure Pgm, StmPtr
MsgBuffer LocalVar                               ; Local copy of statement MsgBuffer.
Number LocalVar Size=20
     MOV ESI,[%StmPtr]
     MOV EBX,[%Pgm]
     MOV EAX,[ESI+STM.MsgBuffer]
     MOV [%MsgBuffer],EAX
     JNSt [EBX+PGM.Status],pgmEnvelope,.10:     ; Envelope program is listed only if something was emitted to it.
     JNSt [EBX+PGM.Status],pgmEnvelDirty,.90:
.10: BufferStore$ [%MsgBuffer],=B'|        **** ListMap "'
     BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt.OutFilePtr],[EBX+PGM.Pgmopt.OutFileSize]
     BufferStore$ [ESI+STM.MsgBuffer],=B'",model='
     MOV EAX,pgmoptModelMask
     AND EAX,[EBX+PGM.Pgmopt.Status]
     MOV EDX,ESI
     Invoke DictSearchByData::,DictProgramModels::,EAX ; Returns ESI=^DICT.
     XCHG EDX,ESI
     BufferStore [ESI+STM.MsgBuffer],[EDX+DICT.Ptr],[EDX+DICT.Size]
     BufferStore$ [ESI+STM.MsgBuffer],=B',groups='
     ; Count linked groups and segments.
     SUB EDI,EDI                                ; Group counter.
     SUB EDX,EDX                                ; Segment counter.
     BufferRetrieve [EBX+PGM.SegOrdBuffer]
     SHR ECX,2
     JZ .22:
.13: LODSD
     JNSt [EAX+SSS.Status],sssGroup,.16:
     INC EDI
.16: JNSt [EAX+SSS.Status],sssSegment,.19:
     INC EDX
.19: LOOP .13:
.22: MOV EAX,EDI                                ; Number of groups in image.
     LEA EDI,[%Number]
     MOV ECX,EDI
     StoD EDI
     SUB EDI,ECX                                ; ECX,EDI is now decimal number of groups.
     MOV ESI,[%StmPtr]
     BufferStore [%MsgBuffer],ECX,EDI
     BufferStore$ [%MsgBuffer],=B',segments='
     MOV EAX,EDX                                ; Number of segments in image.
     LEA EDI,[%Number]
     MOV ECX,EDI
     StoD EDI
     SUB EDI,ECX                                ; ECX,EDI is now decimal number of segments.
     BufferStore [%MsgBuffer],ECX,EDI
     BufferStore$ [%MsgBuffer],=B',entry='      ; List entry=.
     CMPD [EBX+PGM.Pgmopt.EntrySize],0
     JNZ .32:                                   ; Use %^ENTRY if not empty.
     BufferRetrieve [EBX+PGM.SegOrdBuffer]
     SHR ECX,2
     JZ .29:
.25: LODSD
     JSt [EAX+SSS.Purpose],sssPurposeCODE,.27:
     LOOP .25:
.27: MOV ESI,[EAX+SSS.NamePtr]
     MOV ECX,[EAX+SSS.NameSize]
.29: MOV EAX,[EBX+PGM.Pgmopt.Status]
     AND AL,pgmoptFormatMask
     CMP AL,pgmoptCOM
     JNE .32:
     BufferStoreByte [%MsgBuffer],'['
     BufferStore [%MsgBuffer],ESI,ECX
     BufferStoreWord [%MsgBuffer],']:'
     BufferStore$ [%MsgBuffer],=B'00000100h'
     JMPS .34:
.32: BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt.EntryPtr],[EBX+PGM.Pgmopt.EntrySize]
.34: BufferStore$ [%MsgBuffer],=B',stack='      ; List stack=.
     MOV EAX,[%Pgm]                             ; Find the last STACK group|segment to EDX.
     XOR EDX,EDX
     BufferRetrieve [EAX+PGM.SegOrdBuffer]
     SHR ECX,2
     JECXZ .49:
.37: LODSD
     JNSt [EAX+SSS.Purpose],sssPurposeSTACK,.40:
     MOV EDX,EAX
     JSt [EDX+SSS.Status],sssGroup,.42:         ; Prefer the stack group.
.40: LOOP .37:
     TEST EDX                                   ; Either 1st stack group or the last stack segment.
     JZ .49:                                    ; Display nothing when no stack.
     MOV EDI,EDX                                ; ^SSS stack.
.42: BufferStoreByte [%MsgBuffer],'['
     BufferStore [%MsgBuffer],[EDX+SSS.NamePtr],[EDX+SSS.NameSize]
     BufferStoreWord [%MsgBuffer],']:'
     MOV EAX,[EBX+PGM.Pgmopt.Status]
     AND EAX,pgmoptFormatMask
     PUSH .Dispatched:
     Dispatch AL,%PfPgmoptList
.pgmoptRSRC:
.pgmoptPE:
.pgmoptDLL:
.pgmoptCOFF:
.pgmoptLIBCOF:
.pgmoptELF:
.pgmoptELFX:
.pgmoptELFSO:
     MOV EAX,[EBX+PGM.Pgmopt.SizeOfStackCommitLow]
     MOV EDX,[EBX+PGM.Pgmopt.SizeOfStackCommitHigh]
     RET
.pgmoptLIBOMF:
.pgmoptOMF:
.pgmoptMZ:                                       ; Stack pointer SP equals the size of the last stack segment.
     MOV EDI,EDX
     MOV EAX,[EDI+SSS.TopLow]
     MOV EDX,[EDI+SSS.TopHigh]
     SUB EAX,[EDI+SSS.BottomLow]
     SBB EDX,[EDI+SSS.BottomHigh]
     RET
.pgmoptBIN:
.pgmoptBOOT:
.pgmoptCOM:                                      ; Stack pointer is fixed to the end of [COM] segment.
     MOV EAX,0xFFFE
     SUB EDX,EDX
     RET
.Dispatched:                                     ; EDX:EAX=stack size.
     LEA EDI,[%Number]
     MOV ECX,EDI
     TEST EDX
     JZ .46:
     XCHG EAX,EDX                               ; Stack size is bigger than 4G.
     StoH EDI,Size=8
     XCHG EDX,EAX
.46: StoH EDI,Size=8
     SUB EDI,ECX
     BufferStore [%MsgBuffer],ECX,EDI
     BufferStoreByte  [%MsgBuffer],'h'
.49: BufferStoreWord [%MsgBuffer],0x0A0D        ; Terminate ListMap header line.
     BufferRetrieve [EBX+PGM.SegOrdBuffer]      ; List groups and segments, one per line.
     SHR ECX,2
     JZ .90:
.52: LODSD                                      ; The loop .52: .. .85: displays all ECX groups and their segments.
.55: PUSH ECX,ESI
       MOV ESI,EAX ; ^SSS.
       JNSt [ESI+SSS.Status],sssGroup|sssSegment,.85: ; Omit extern pseudosegments.
       BufferStore$ [%MsgBuffer],=B'|        '
       JNSt [ESI+SSS.Status],sssSegment,.58:
       BufferStoreWord [%MsgBuffer],'  '        ; Indent segment by two spaces against its group.
.58:   BufferStoreByte [%MsgBuffer],'['
       BufferStore [%MsgBuffer],[ESI+SSS.NamePtr],[ESI+SSS.NameSize]
       BufferStore$ [%MsgBuffer],=B'],FA='      ; Display File Address.
       MOV EAX,[ESI+SSS.BottomFA]
       LEA EDI,[%Number]
       MOV EDX,EDI
       StoH EDI,Size=8
       SUB EDI,EDX
       BufferStore [%MsgBuffer],EDX,EDI
       MOV EBX,[%Pgm]
       MOV EAX,[EBX+PGM.Pgmopt.Status]
       AND AL,pgmoptFormatMask
.VA?RVA:PROC                                     ; Create namespace for dispatching of local .pgmopt* labels.
        Dispatch AL,pgmoptBIN,pgmoptBOOT,pgmoptPE,pgmoptDLL,pgmoptELFX ; Program formats which link at absolute VA rather than RVA.
        BufferStore$ [%MsgBuffer],=B'h,RVA='                           ; Other formats link at RVA relative to ImageBase.
        JMPS PgmListMap.63:
.pgmoptBIN:
.pgmoptBOOT:
.pgmoptPE:
.pgmoptDLL:
.pgmoptELFX:
       ENDP .VA?RVA:
       BufferStore$ [%MsgBuffer],=B'h,VA='
.63:   LEA EDI,[%Number]
       MOV EDX,[ESI+SSS.BottomHigh]
       MOV EAX,[ESI+SSS.BottomLow]
       TEST EDX
       MOV EDX,EDI
       JZ .65:                                   ; VA is displayed as 32bit or 64bit.  
       XCHG EAX,ECX                              ; Temporary save EAX to ECX.
         MOV EAX,[ESI+SSS.BottomHigh]
         StoH EDI,Size=8
       XCHG ECX,EAX
.65:   StoH EDI,Size=8
       SUB EDI,EDX
       BufferStore [%MsgBuffer],EDX,EDI
       BufferStore$ [%MsgBuffer],=B'h,size='
       LEA EDI,[%Number]
       MOV EAX,[ESI+SSS.TopLow]
       MOV EDX,[ESI+SSS.TopHigh]
       SUB EAX,[ESI+SSS.BottomLow]
       SBB EDX,[ESI+SSS.BottomHigh]
       LEA EDI,[%Number]
       MOV EDX,EDI
       StoH EDI,Size=8
       SUB EDI,EDX
       PUSH EAX
         BufferStore [%MsgBuffer],EDX,EDI
         BufferStoreWord [%MsgBuffer],'h='
       POP EAX
       LEA EDI,[%Number]
       MOV EDX,EDI
       StoD EDI
       SUB EDI,EDX
       BufferStore [%MsgBuffer],EDX,EDI
.67:   JNSt [ESI+SSS.Status],sssGroup,.79:
       BufferStore$ [%MsgBuffer],=B',group'     ; List the names of all group members.
       PUSH ECX,ESI
         MOV EDI,ESI                            ; Enumerate names of all segments which belong to the group EDI.
         MOV EBX,[%Pgm]
         BufferRetrieve [EBX+PGM.SegOrdBuffer]
         SHR ECX,2
.70:     LODSD
         MOV EBX,EAX
         JNSt [EBX+SSS.Status],sssSegment,.76:
         CMP EDI,[EBX+SSS.GroupPtr]
         JNE .76:
.73:     BufferStoreWord [%MsgBuffer],' ['
         BufferStore [%MsgBuffer],[EBX+SSS.NamePtr],[EBX+SSS.NameSize]
         BufferStoreByte [%MsgBuffer],']'
.76:     LOOP .70:
       POP ESI,ECX
       JMP .82:
.79:   BufferStore$ [%MsgBuffer],=B',width='
       MOV EBX,[ESI+SSS.Status]
       MOV EAX,sssWidthMask
       AND EAX,EBX
       LEA EDI,[%Number]
       MOV EDX,EDI
       SHR EAX,20
       StoD EDI
       SUB EDI,EDX
       BufferStore [%MsgBuffer],EDX,EDI
       BufferStore$ [%MsgBuffer],=B',align='
       MOV EAX,[ESI+SSS.Alignment]
       MOV EDI,EDX
       CMP EAX,8
       JNA .80:                                  ; Stronger alignment than 8 display as hexa.
       StoH EDI,Size=4
       MOV AL,'h'
       STOSB
       JMPS .81:
.80:   StoD EDI
.81:   SUB EDI,EDX
       BufferStore [%MsgBuffer],EDX,EDI
       BufferStore$ [%MsgBuffer],=B',purpose='
       Invoke EaBufferReserve::,PgmListMap
       Invoke SssPurposeToText::,[ESI+SSS.Purpose],EAX
       BufferRetrieve EAX
       DEC ECX                                   ; Omit zero terminator.
       BufferStore [%MsgBuffer],ESI,ECX
       Invoke EaBufferRelease::,EAX
.82:   BufferStoreWord [%MsgBuffer],0x0A0D
.85: POP ESI,ECX
.88: LOOP .52:                                   ; The next group/segment.
.90: EndProcedure PgmListMap
↑ PgmListGlobals Pgm, StmPtr
PgmListGlobals appends aplhabetical list of global symbols to listing, below ENDPROGRAM statement and below ListMap.
It is invoked only when PROGRAM LISTGLOBALS=ON.
Input
Pgm is pointer to PGM - completely assembled, linked and formated program.
StmPtr is pointer to STM object with ENDPROGRAM statement.
Output
-
Error
-
See also
PgmListMap.
Invokes
EaBufferRelease EaBufferReserve EaBufferSort SymFrameAddress
Invoked by
PgmDestroy
PgmListGlobals Procedure Pgm, StmPtr
MsgBuffer LocalVar ; Local copy of statement's MsgBuffer borrowed to display the list of symbols.
InpBuffer LocalVar ; Temporary buffer with pointers to global symbols.
OutBuffer LocalVar ; Temporary buffer with pointers to global symbols.
CountGlb  LocalVar ; Counter of global symbols.
CountPub  LocalVar ; Counter of public symbols.
CountExt  LocalVar ; Counter of extern symbols.
CountExp  LocalVar ; Counter of export symbols.
CountImp  LocalVar ; Counter of import symbols.
Number    LocalVar Size=12 ; Room for decadic format of counters.
     ClearLocalVar
     MOV ESI,[%StmPtr]
     MOV EBX,[%Pgm]
     MOV EAX,[ESI+STM.MsgBuffer]
     MOV [%MsgBuffer],EAX
     JNSt [EBX+PGM.Status],pgmEnvelope,.10:
     JNSt [EBX+PGM.Status],pgmEnvelDirty,.90:    ; Do not display header when source envelope was not used.
.10: BufferStore$ [%MsgBuffer],=B'|        **** ListGlobals "' ; Display the header.
     BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt.OutFilePtr],[EBX+PGM.Pgmopt.OutFileSize]
     BufferStore$ [%MsgBuffer],=B'",Global='
     Invoke EaBufferReserve::,PgmListGlobals
     MOV [%InpBuffer],EAX
     Invoke EaBufferReserve::,PgmListGlobals
     MOV [%OutBuffer],EAX
      ; Step 1: store pointers to all unresolved symbols with nonstandard (global) scope to input buffer in the loop .13: .. .16:
     ListGetFirst [EBX+PGM.SymList]
     JZ .40:
.13: JSt [EAX+SYM.Status],symResolved,.16:
     JNSt [EAX+SYM.Status],symScopeMask,.16:     ; Skip standard private symbols.
     BufferStoreDword [%InpBuffer],EAX
.16: ListGetNext EAX
     JNZ .13:
     ; Step 2: Sort global symbols alphabeticaly by name.
     Invoke EaBufferSort::,[%InpBuffer]
     ; Step 3: merge public and extern/import symbols with the same name (they may have been left unresolved),
     ;    count and copy them to the output buffer in the loop .20: .. .43:.
     BufferRetrieve [%InpBuffer]
     LEA EDX,[ESI+ECX]                           ; End of pointer array.
.20: CMP ESI,EDX
     JNB .40:
     LODSD
     CMP ESI,EDX
     JNB .26:
     MOV EBX,[ESI]                               ; EAX and EBX are now pointers to neighbour symbols, perhaps with the same name.
     Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],[EBX+SYM.NamePtr],[EBX+SYM.NameSize]
     JNE .26:
     ; Both symbols EAX and EBX have the same name. Let's find which is the public|export one.
     ADD ESI,4 ; Skip the 2nd pointer because they will be merged.
     JSt [EAX+SYM.Status],symPublic|symExport,.23:
     XCHG EAX,EBX
.23: MOV EBX,[EBX+SYM.Status]                    ; EAX is public|export, EBX is extern|import.
     AND EBX,symImport+symExtern
     SetSt EBX,symGlobal
     SetSt [EAX+SYM.Status],EBX                  ; Symbol EAX will be listed as global, too.
.26: BufferStoreDword [%OutBuffer],EAX
.30: ; Update global/public/extern/export/import counter.
     MOV EBX,[EAX+SYM.Status]
     LEA ECX,[%CountExp]
     JSt EBX,symExport,.36:                      ; Count as 'X'.
     LEA ECX,[%CountImp]
     JSt EBX,symImport,.36:                      ; Count as 'I'.
     LEA ECX,[%CountPub]
     JNSt EBX,symPublic,.32:
     JNSt EBX,symExtern,.36:                     ; Count as 'P' if not external, too.
     JMP .34:                                    ; Count as 'G', i.e. public and external.
.32: LEA ECX,[%CountExt]
     JSt EBX,symExtern,.36:                      ; Count as 'E'.
.34: LEA ECX,[%CountGlb]
.36: INCD [ECX]                                  ; Increment one of five counters.
     JMP .20:                                    ; The next symbol.
.40: MOV EAX,[%CountGlb]
     LEA EDI,[%Number]
     MOV EDX,EDI
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStore$ [%MsgBuffer],=B',Public='
     MOV EAX,[%CountPub]
     LEA EDI,[%Number]
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStore$ [%MsgBuffer],=B',Extern='
     MOV EAX,[%CountExt]
     LEA EDI,[%Number]
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStore$ [%MsgBuffer],=B',eXport='
     MOV EAX,[%CountExp]
     LEA EDI,[%Number]
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStore$ [%MsgBuffer],=B',Import='
     MOV EAX,[%CountImp]
     LEA EDI,[%Number]
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStoreWord [%MsgBuffer],0x0A0D
      ; List symbols, sorted by name.
     BufferRetrieve [%OutBuffer]
     SAR ECX,2
     JZ .80:
.43: LODSD ; List properties of global symbol EAX.
     PUSH ECX,ESI
       MOV EBX,EAX
       BufferStore$ [%MsgBuffer],=B'|        '
       BufferStore [%MsgBuffer],[EBX+SYM.NamePtr],[EBX+SYM.NameSize]
       JSt [EBX+SYM.Status],symForwarded,.68:
       JNSt [EBX+SYM.Status],symImport,.53:
       JNSt [EBX+SYM.Status],symImportedByOrd,.46:
       ; Symbol imported by ordinal number will be listed as Name(OrdinalNr).
       BufferStoreByte [%MsgBuffer],'('          ; List ordinal number in parenthesis.
       MOV EAX,[EBX+SYM.OrdinalNr]
       LEA EDI,[%Number]
       MOV EDX,EDI
       StoD EDI
       SUB EDI,EDX
       BufferStore [%MsgBuffer],EDX,EDI
       JMP .50:
.46:   MOV ECX,[EBX+SYM.InterNameSize]
       JECXZ .53:                                ; If internal name is empty, do not display it.
       ; Symbol imported by name will be listed as Name{InterName} if Name<>InterName.
       Compare [EBX+SYM.NamePtr],[EBX+SYM.NameSize],[EBX+SYM.InterNamePtr],ECX
       JE .53:                                   ; Do not display internal name when it is empty or the same.
       BufferStoreByte [%MsgBuffer],'{'          ; List internal name in curly brackets.
       BufferStore [%MsgBuffer],[EBX+SYM.InterNamePtr],[EBX+SYM.InterNameSize]
.50:   BufferStoreByte [%MsgBuffer],'}'
.53:   BufferStoreWord [%MsgBuffer],',['         ; List group name in brackets.
       Invoke SymFrameAddress::,EBX,[%Pgm]       ; Returns EDX:EAX=offset, ECX=^group.
       JECXZ .56:                                ; Scalar symbol has empty segment name.
       BufferStore [%MsgBuffer],[ECX+SSS.NamePtr],[ECX+SSS.NameSize]
.56:   BufferStoreWord [%MsgBuffer],']:'
       LEA EDI,[%Number]
       MOV EDX,EDI
       StoH EDI,Size=8
       SUB EDI,EDX
       BufferStore [%MsgBuffer],EDX,EDI          ; List symbol offset.
       BufferStoreWord [%MsgBuffer],'h,'
       MOV EDX,[%Pgm]
       MOV EAX,[EDX+PGM.Pgmopt.Status]
       JNSt EAX,pgmoptExecutable|pgmoptImage,.70:
       AND AL,pgmoptFormatMask
       CMP AL,pgmoptBIN
       JE .60:
       CMP AL,pgmoptPE
       JE .60:
       CMP AL,pgmoptDLL
       JE .60:
       CMP AL,pgmoptELFX
       JE .60:
       BufferStoreByte [%MsgBuffer],'R'          ; Display VA= in images, RVA= in linkables.
.60:   BufferStore$ [%MsgBuffer],=B'VA='
.63:   SUB EAX,EAX
       MOV ECX,[EBX+SYM.Section]
       JECXZ .66:
       MOV ECX,[ECX+SSS.SegmPtr]
       JECXZ .66:
       MOV EAX,[ECX+SSS.BottomLow]
.66:   ADD EAX,[EBX+SYM.OffsetLow]
       LEA EDI,[%Number]
       MOV EDX,EDI
       StoH EDI,Size=8
       SUB EDI,EDX
       BufferStore [%MsgBuffer],EDX,EDI          ; List symbol VA.
       BufferStoreByte [%MsgBuffer],'h'
.68:   BufferStoreByte [%MsgBuffer],','
       ; Display scope=.
.70:   BufferStore$ [%MsgBuffer],=B"scope='"
       MOV ECX,[EBX+SYM.Status]
       MOV AL,'X'
       JSt ECX,symExport,.76:
       MOV AL,'I'
       JSt ECX,symImport,.76:
       MOV AL,'P'
       JNSt ECX,symPublic,.72:
       JNSt ECX,symExtern,.76:
       JMP .74:
.72:   MOV AL,'E'
       JSt ECX,symExtern,.76:
.74:   MOV AL,'G'
.76:   MOV AH,"'"
       BufferStoreWord [%MsgBuffer],EAX
       ; Display lib= of imported and forwarded symbols.
       JNSt [EBX+SYM.Status],symImport|symForwarded,.78:
       BufferStore$ [%MsgBuffer],=B',lib="'
       MOV ECX,[EBX+SYM.DllNameSize]
       MOV ESI,[EBX+SYM.DllNamePtr]
       TEST ECX
       JNZ .77:
       MOV ESI,=B"%EuroasmDefaultDllName"
       MOV ECX,%EuroasmDefaultDllNameSize
.77:   BufferStore [%MsgBuffer],ESI,ECX
       BufferStoreByte [%MsgBuffer],'"'
.78:   ; Display fwd= of forwarded symbols.
       JNSt [EBX+SYM.Status],symForwarded,.79:
       BufferStore$ [%MsgBuffer],=B',fwd='
       BufferStore [%MsgBuffer],[EBX+SYM.InterNamePtr],[EBX+SYM.InterNameSize]
.79:   BufferStoreWord [%MsgBuffer],0x0A0D
     POP ESI,ECX
     LOOP .43:                                   ; The next global symbol.
.80: Invoke EaBufferRelease::,[%OutBuffer]
     Invoke EaBufferRelease::,[%InpBuffer]
.90:EndProcedure PgmListGlobals
↑ PgmEvalEntry PgmPtr
PgmEvalEntry evaluates program option ENTRY= which may be specified as an expression of symbol name, immediate offset value, immediate far address.
Evaluated expression is stored to Pgm.EntryExp. If program entry is not specified, the output expression is left as is (clear).
Input
PgmPtr is a pointer to the program PGM.
Output
Program.EntryExp is evaluated.
Error
Evaluation error is signalized with Pgm.EntryExp.Status='#'.
See also
ExpReportError
Invokes
ExpEval ExpReportError
Invoked by
PfbootCompile PfcomCompile PfmzCompile PgmGetEntryAddress
PgmEvalEntry Procedure PgmPtr
     MOV EBX,[%PgmPtr]
     LEA EDI,[EBX+PGM.EntryExp]
     MOV ECX,[EBX+PGM.Pgmopt.EntrySize]
     MOV ESI,[EBX+PGM.Pgmopt.EntryPtr]
     StripSpaces ESI,ECX
     JECXZ .90:                            ; If the program option ENTRY= is empty, leave PGM.EntryExp empty as well.
     Invoke ExpEval::,EDI,ESI,ECX,[EBX+PGM.CurrentStm]
     Invoke ExpReportError::,EDI
     JC .90:
     MOV ECX,[EDI+EXP.Sym]
     JECXZ .90:
     SetSt [ECX+SYM.Status],symEntry+symGlobal+symUsed+symProc ; If the ENTRY= referred some symbol, set it a global scope.
     MOV EAX,[ECX+SYM.SymbPtr]
     TEST EAX
     JZ .30:
     MOV ECX,EAX
.30: MOV EAX,[ECX+SYM.Section]
     JNSt [EAX+SSS.Status],sssLinked,.50:
     MOV EDX,[EAX+SSS.BottomHigh]
     MOV EAX,[EAX+SSS.BottomLow]
     ADD EAX,[ECX+SYM.OffsetLow]
     ADC EDX,[ECX+SYM.OffsetHigh]
     MOV [EDI+EXP.Low],EAX
     MOV [EDI+EXP.High],EDX
     MOVD [EDI+EXP.Seg],0
     MOVB [EDI+EXP.Status],'N'             ; When the entry symbol is in linked segment, change address symbol to a numeric one.
.50: JNSt [ECX+SYM.Status],symExtern,.90:
     MOV EDX,[ECX+SYM.Section]             ; Symbol ECX is extern, try to resolve it.
     MOV ECX,[EDX+SSS.SymPtr]
     JECXZ .90:
     MOV [EDI+EXP.Sym],ECX                 ; Replace the extern symbol with its homonymous public symbol.
     SetSt [ECX+SYM.Status],symEntry+symGlobal+symUsed+symProc ; If the ENTRY= referred some symbol, set it a global scope.
.90:EndProcedure PgmEvalEntry
↑ PgmGetEntryAddress PgmPtr
PgmGetEntryAddress returns virtual address of program entry point or error when program format was executable and PROGRAM ENTRY= was empty or if it evaluated with error.
Input
PgmPtr is a pointer to PGM whose PGM.EntryExp contains PROGRAM ENTRY= parameter evaluated by PgmEvalEntry.
Output
CF=0
EDX:EAX= VA of entry point.
Error
CF=1,
EDX:EAX=0,
Errors E7710, E7711 are reported with macro Msg.
Invoked by
PfelfEhdr
Invokes
PgmEvalEntry
PgmGetEntryAddress Procedure PgmPtr
     MOV EBX,[%PgmPtr]
     XOR EAX,EAX
     XOR EDX,EDX
     CMP [EBX+PGM.Pgmopt.EntrySize],EAX
     JNZ .Ent:
     JNSt [EBX+PGM.Pgmopt.Status],pgmoptExecutable,.End:
     JSt [EBX+PGM.Status],pgmEntryReported,.Err:
     Msg '7710'                                  ; "PROGRAM ENTRY=" is mandatory in this program format.
     SetSt [EBX+PGM.Status],pgmEntryReported
     JMP .Err:                                   ; PROGRAM ENTRY="!1S" symbol was not found.
.Ent:Invoke PgmEvalEntry,EBX
     LEA ESI,[EBX+PGM.EntryExp]
     MOV ECX,[ESI+EXP.Status]
     Dispatch CL,'A','N'
     JSt [EBX+PGM.Status],pgmEntryReported,.Err:
     LEA ECX,[EBX+PGM.Pgmopt.EntryPtr]
     Msg '7711',ECX                              ; PROGRAM ENTRY="!1S" symbol was not found.
     SetSt [EBX+PGM.Status],pgmEntryReported
.Err:STC                                         ; Other ENTRY evaluated types than number or symbol are wrong.
     JMP .End:                                   ; Entry was evaluated to error status '#' (already reported) or other nonsense.
.A:  MOV ECX,[ESI+EXP.Seg]
     JECXZ .End:
     ADD EAX,[ECX+SSS.BottomLow]
     ADC EDX,[ECX+SSS.BottomHigh]
.N:  ADD EAX,[ESI+EXP.Low]
     ADC EDX,[ESI+EXP.High]
.End:MOV [%ReturnEAX],EAX
     MOV [%ReturnEDX],EDX
    EndProcedure PgmGetEntryAddress
↑ PgmCombine BasePgm

PgmCombine will merge linkable programs (modules) which were loaded from disk file(s) by PfLoad as a result of pseudoinstruction LINK . Pointers to those modules has been already stored on Pgm.ModulePgmList.

This procedure combines only modules marked as pgmSelected in their PGM.Status by PgmSelectModules (smart linking).

Pointers to combined modules are left on Pgm.ModulePgmList but the emitted contents of homonymous public segments is merged to BasePgm.

Input
BasePgm Pointer to the main (base) program which is already assembled and which the modules are linked to.
BasePgm may be empty (no segments, no symbols) when the program is only a linker-script.
Output
Selected loaded programs (modules) from BasePgm.ModulePgmList are combined with the BasePgm.
Error
Errors are reported with macro Msg.
Invoked by
PfOutput
Invokes
PgmResizeGroups RelocCombine SssCombine SssResizeGroup SssResolve SymCombine
PgmCombine Procedure BasePgm
    MOV EBX,[%BasePgm]
    ; Step 1: Merge SSS externs, segments and groups from loaded modules to BasePgm, including their emited contents and relocations.
    ; Bottom and Top of merged public segment is elevated, but symbols and relocations are not copied yet to the BasePgm.
    ListGetFirst [EBX+PGM.ModulePgmList]
    JZ .90:                                      ; Do nothing when no module is linked to the BasePgm.
.10:MOV EDI,EAX                                  ; EDI is now one of the modules which were loaded by LINK statement.
    JNSt [EDI+PGM.Status],pgmSelected,.18:       ; Skip the unused library module (smart linking).
    ListGetFirst [EDI+PGM.SssList]               ; EAX is now one of the SSS objects of linked module.
    JZ .18:
.14:Invoke SssCombine::,EAX,EBX                  ; Combine linked SSS object EAX to the main program EBX (also merge its Emit&RelocBuffer unchanged).
.16:ListGetNext EAX
    JNZ .14:                                     ; The next SSS object of linked module EDI.
.18:ListGetNext EDI                              ; The next linked module.
    JNZ .10:
    Invoke SssResolve::,EBX                      ; Update references SSS.SegmPtr, SSS.GroupPtr, SSS.SymPtr  in all SSS objects which were combined to BasePgm.
    ListGetFirst [EBX+PGM.ModulePgmList]
.38:MOV EDI,EAX                                  ; EDI points to the combined module.
    JNSt [EDI+PGM.Status],pgmSelected,.45:       ; Skip the unused library module (smart linking).
    ListGetFirst [EDI+PGM.SymList]
    JZ .45:
.40:Invoke SymCombine::,EAX,EBX                  ; Reassign symbols from linked modules to BasePgm, elevate their offsets.
    ListGetNext EAX                              ; The next symbol.
    JNZ .40:
.45:ListGetNext EDI                              ; The next linked module.
    JNZ .38:

    ; Step 2: update relocations in all segments of the base program EBX.
    ListGetFirst [EBX+PGM.SssList]               ; Resolve relocations from base program.
    JZ .36:
.20:BufferRetrieve [EAX+SSS.RelocBuffer]         ; Relocations from linked modules were copied here verbatim.
    JC .32:
    ADD ECX,ESI                                  ; ECX is now the end of array of RELOC records.
.24:CMP ESI,ECX
    JNB .32:
    JSt [ESI+RELOC.Status],relocIgnore,.28:
    Invoke RelocCombine::,ESI,EBX
.28:ADD ESI,SIZE# RELOC
    JMP .24:                                     ; The next RELOC record.
.32:ListGetNext EAX                              ; The next segment.
    JNZ .20:
.36:
    ; Update virtual addresses of all groups combined in BasePgm.
    ListGetFirst [EBX+PGM.SssList]
    JZ .60:
.50:JNSt [EAX+SSS.Status],sssGroup, .55:
    Invoke SssResizeGroup::,EAX,EBX
.55:ListGetNext EAX                              ; The next SSS.
    JNZ.50:
.60:; Step 4: combine entry point from linked modules (if any)  in its unevaluated source form, and copy it to the BasePgm.
    ListGetFirst [EBX+PGM.ModulePgmList]
.65:MOV EDI,EAX                                  ; EDI points to the linked module.
    JNSt [EDI+PGM.Status],pgmSelected,.75:       ; Skip the unused library module (smart linking).
    MOV ECX,[EDI+PGM.Pgmopt.EntrySize]
    JECXZ .75:                                   ; Skip when the module EDI has no entry point in source form.
    MOV EAX,[EBX+PGM.Pgmopt.EntrySize]
    MOV ESI,[EDI+PGM.Pgmopt.EntryPtr]
    StripColons ESI,ECX
    TEST EAX                                     ; Check if ENTRY= was already specified in the base program EBX.
    JZ .70:                                      ; Skip when the base pgm EBX has vacant entry.
    MOV EDX,[EBX+PGM.Pgmopt.EntryPtr]
    StripColons EDX,EAX
    Compare ESI,ECX,EDX,EAX
    JE .75:                                      ; Do nothing when ENTRY= in module EDI is identical with ENTRY= in the base program EBX.
.W3925:
    LEA ECX,[EDI+PGM.Pgmopt.EntryPtr]
    LEA EDX,[EBX+PGM.Pgmopt.EntryPtr]
    Msg '3925',ECX,EDI,EDX,EBX                   ; Entry "!1S" specified in linkable module "!2S" collides with the entry "!3S"  in "!4S". Ignored.
    JMPS .75:
.70:MOV [EBX+PGM.Pgmopt.EntryPtr],ESI            ; Copy the entry point when it wasn't specified in the base program yet.
    MOVW [EBX+ECX+PGM.Pgmopt.EntryPtr],'::'      ; Append two colons to entry name
    INC ECX,ECX
    MOV [EBX+PGM.Pgmopt.EntrySize],ECX
.75:ListGetNext EDI                              ; The next linked module.
    JNZ .65:
;;    ; Step 5: Resolve symbols from loaded modules.
.80:MOV [EBX+PGM.ModulePgmList],0                ; Invalidate the list of combined modules.
.90:Invoke PgmResizeGroups,EBX
   EndProcedure PgmCombine
↑ PgmDetectImportModule Pgm

PgmDetectImportModule tries to detect if the Pgm (just loaded module) is a module from import library, i.e. if it contains import info only, and it will set the flag Pgm.Status:pgmImpLibMember in this case.

Module is pure import library member if

  1. Pgm has not more than 3 symbols,
  2. Pgm has exactly one segment,
  3. the segment name contains the word 'IMPORT' or 'IDATA' (case insensitive),
  4. emitted segment data are 6 or 7 bytes long (indirect proxy JMPN).
Import libraries might have been created by a third-party librarian, such as ALIB.exe, The only code they have is an indirect proxy jump through IAT table to dynamically loaded function, and a public symbol referring to this proxy jump. Imported module may also contain an extern symbol with decorated internal name.
Input
Pgm Pointer to a loaded module PGM.
Output
pgmImpLibMember flag is modified in Pgm.Status.
Error
-
See also
PfDetect
Invoked by
PfomfLoadModule
Invokes
EaBufferRelease EaBufferReserve
PgmDetectImportModule Procedure Pgm
    MOV EBX,[%Pgm]
    RstSt [EBX+PGM.Status],pgmImpLibMember       ; First assume it is an ordinary object module.
    SUB ECX,ECX                                  ; Count symbols in module EBX into counter ECX.
    ListGetFirst [EBX+PGM.SymList]
    JZ .90:                                      ; No symbols exits, its not an import library member.
.10:INC ECX
    ListGetNext EAX
    JNZ .10:
    CMP ECX,3
    JA .90:                                      ; Too many symbols, not an import library member.
    SUB ECX,ECX                                  ; Count segments in module EBX into counter ECX.
    ListGetFirst [EBX+PGM.SssList]
    JZ .90:                                      ; No segments exist, its not an import library member.
.20:JNSt [EAX+SSS.Status],sssSegment,.30:
    INC ECX
    MOV EDX,EAX                                  ; Remember the segment in EDX.
.30:ListGetNext EAX
    JNZ .20:
    CMP ECX,1
    JNE .90:                                     ; Too many segments, its not an import library member.
    MOV ECX,[EDX+SSS.NameSize]                   ; EDX is the one and only segment.
    MOV ESI,[EDX+SSS.NamePtr]                    ; Convert the segment name ESI,ECX to uppercase.
    CMP ECX,5
    JB .90:                                      ; Segment name is too short, not IDATA/IMPORT.
    Invoke EaBufferReserve::,PgmDetectImportModule
    MOV EBX,EAX
    BufferStore EBX,ESI,ECX
    BufferRetrieve EBX
    MOV EDI,ESI
.40:LODSB                                        ; Convert the letter to upper case.
    CMP AL,'a'
    JB .50:
    CMP AL,'z'
    JA .50:
    SUB AL,'a'-'A'
.50:STOSB
    LOOP .40:
    BufferRetrieve EBX                           ; ESI,ECX is now uppercased segment name.
    Invoke EaBufferRelease::,EBX
    MOV EDI,ESI                                  ; Search the name for 'IMPORT' or 'IDATA'.
    MOV AL,'I'
.60:REPNE SCASB
    JNE .90:
    CMP ECX,4
    JB .90:
    CMPD [EDI],'DATA'
    JE .70:
    CMP ECX,5
    JB .90:
    CMPB [EDI],'M'
    JNE .60:
    CMPD [EDI+1],'PORT'
    JNE .60:
.70:BufferRetrieve [EDX+SSS.EmitBuffer]
    CMP ECX,7
    JA .90:
    CMP CL,6
    JB .90:
    MOV EBX,[%Pgm]                               ; Segment name contains 'IMPORT' or 'IDATA'
    SetSt [EBX+PGM.Status],pgmImpLibMember       ;   and its code is 6..7 bytes long. Module is an import library member.
.90:EndProcedure PgmDetectImportModule
↑ PgmCheckDirty Pgm
PgmCheckDirty inspects the program if something was emitted or linked to it. Program is dirty when at least one its section is dirty or when Pgm.LinkFilesNr is nonzero.
Input
Pgm Pointer to PGM of the checked program.
Output
CF=0 if empty, 1 if dirty (some code, data or label was emitted).
Error
-
See also
SssCheckDirty.
Invokes
SssCheckDirty
Invoked by
PgmDestroy PseudoENDPROGRAM
PgmCheckDirty Procedure Pgm
     MOV EBX,[%Pgm]
      MOV ECX,[EBX+PGM.LinkFilesNr]
     JECXZ .10:
     STC
     JMP .90: ; Dirty.
 .10:ListGetFirst [EBX+PGM.SssList]
     JZ .NotDirty:
 .20:Invoke SssCheckDirty::,EAX,EBX
     JC .90: ; Dirty
     ListGetNext EAX
     JNZ .20:
 .NotDirty:
     CLC
.90: EndProcedure PgmCheckDirty
↑ PgmSelectModules Program

PgmSelectModules provides smart linking of modules to executable file. It will inspect each module loaded on Program.ModulePgmList whether some of its public|export symbols is referenced by some extern|import symbol defined in the base program or in other non-import modules, and vice versa. Such module will be marked pgmSelected for later combining. Unreferenced modules from libraries will not be combined and linked.

When the base Program output format is a library (FORMAT=LIBOMF|LIBCOF|ELFSO) instead of executable, all its loaded modules will be marked pgmSelected regardless if they are referenced or not (perhaps they will be referenced later, when the base Program (library) would get linked to an executable format).

When any of the linked programs is a standalone module (FORMAT=COFF|OMF|ELF) and not a library member (pgmoptLibMember=0), it will be marked pgmSelected regardless if its symbols are referenced or not (because it was explicitly required with LINK module.obj).

Input
Program is pointer to the compiled base PGM program, to which it is linked.
Output
Referenced loaded modules are marked with flag PGM.Status:pgmSelected.
Error
-
Invokes
EaBufferRelease EaBufferReserve PgmStoreGlobalSymbols
Invoked by
PfOutput
PgmSelectModules Procedure Program
PubBuf LocalVar ; Pointer to a buffer which contains DWORD pointers to all public|export symbols.
ExtBuf LocalVar ; Pointer to a buffer which contains DWORD pointers to all extern|import symbols.
    Invoke EaBufferReserve::,PgmSelectModules
    MOV [%PubBuf],EAX
    MOV ECX,EAX
    Invoke EaBufferReserve::,PgmSelectModules
    MOV [%ExtBuf],EAX
    MOV EDX,EAX
    MOV EBX,[%Program]
    SetSt [EBX+PGM.Status],pgmSelected           ; The base program is always selected.
    JNSt [EBX+PGM.Pgmopt.Status],pgmoptLibrary,.14: ; Skip when the base program is not a library.
    ListGetFirst [EBX+PGM.ModulePgmList]         ; Mark all modules as pgmSelected when a library is being compiled.
    JZ .90:                                      ; Nothing to do when no module was loaded.
.10:SetSt [EAX+PGM.Status],pgmSelected
    ListGetNext EAX
    JNZ .10:
    JMP .90:
.14:; Collect pointers to global symbols into ExtBuf EDX and PubBuf ECX from all non-import-only modules.
    Invoke PgmStoreGlobalSymbols,EBX,ECX,EDX     ; Collect globals from the base program EBX.
    ListGetFirst [EBX+PGM.ModulePgmList]
    JZ .90:                                      ; Nothing to do when no module was loaded.
.18:JSt [EAX+PGM.Status],pgmImpLibMember,.22:    ; Ignore pure import library members.
    Invoke PgmStoreGlobalSymbols,EAX,ECX,EDX     ; Collect globals from the loaded module EAX.
.22:ListGetNext EAX
    JNZ .18:
.26:; Inspect each module and flag the module as pgmSelected if
    ;    any of its public symbols is referenced by any symbol in ExtBuf, or
    ;    if the module is not a library (it is a standalone COFF|OMF|ELF file).
    ListGetFirst [EBX+PGM.ModulePgmList]
.30:MOV EBX,EAX                                  ; EBX is now the inspected module.
    JSt [EBX+PGM.Status],pgmSelected,.54:        ; Skip inspection when its already referenced.
    JSt [EBX+PGM.Pgmopt.Status],pgmoptLibMember,.34:
    SetSt [EBX+PGM.Status],pgmSelected           ; Select loaded standalone COFF|OMF modules.
    JMP .54:
.34:ListGetFirst [EBX+PGM.SymList]
    JZ .54:                                      ; Skip the module EBX when it has no symbols.
.38:MOV EDI,EAX                                  ; EDI is now a symbol from module EBX.
    JNSt [EDI+SYM.Status],symPublic|symExport,.50:
    BufferRetrieve [%ExtBuf]                     ; EDI is public symbol in loaded module EBX. Find if it is referenced.
    SHR ECX,2
    JZ .58:                                      ; Skip when no external symbols exist.
.42:LODSD                                        ; EAX is now one of external/imported symbols. Compare names of symbols EAX and EDI.
    Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],[EDI+SYM.NamePtr],[EDI+SYM.NameSize]
    JNE .46:
    SetSt [EBX+PGM.Status],pgmSelected           ; The module was referenced, it will be combined and linked.
    JMP .54:                                     ; Break further inspecting of other symbols in module EBX.
.46:LOOP .42:
.50:ListGetNext EDI                              ; The next symbol.
    JNZ .38:
.54:ListGetNext EBX
    JNZ .30:                                     ; The next loaded module.
.58:; Collects pointers to global symbols  into ExtBuf EDX and PubBuf ECX again but this time  from referenced modules only.
    MOV EBX,[%Program]
    MOV ECX,[%PubBuf]
    MOV EDX,[%ExtBuf]
    BufferClear ECX
    BufferClear EDX
    Invoke PgmStoreGlobalSymbols,EBX,ECX,EDX     ; Collect globals from the base program.
    ListGetFirst [EBX+PGM.ModulePgmList]
.62:JNSt [EAX+PGM.Status],pgmSelected,.66:       ; Skip not referenced modules.
    Invoke PgmStoreGlobalSymbols,EAX,ECX,EDX     ; Collect globals from the loaded module.
.66:ListGetNext EAX
    JNZ .62:                                     ; The next module.
    ; Inspect each module and flags it as pgmSelected if any of its public|import symbols is referenced  by any symbol in ExtBuf.
    ListGetFirst [EBX+PGM.ModulePgmList]
.70:MOV EBX,EAX                                  ; EBX is now the inspected module.
    ListGetFirst [EBX+PGM.SymList]
    JZ .88:                                      ; Skip the module EBX when it has no symbols.
.74:MOV EDI,EAX                                  ; EDI is now a symbol from module EBX.
    JNSt [EDI+SYM.Status],symPublic|symImport,.86:
    BufferRetrieve [%ExtBuf]                     ; EDI is public|import symbol in loaded module EBX. Check if it is referenced.
    SHR ECX,2
    JZ .86:
.78:LODSD                                        ; EAX is now one of external/imported symbols. Compare names of symbols EAX and EDI.
    Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],[EDI+SYM.NamePtr],[EDI+SYM.NameSize]
    JNE .82:
    SetSt [EBX+PGM.Status],pgmSelected           ; The module was referenced, it will be combined and linked.
    JMP .88:                                     ; Break further inspecting of other symbols in module EBX.
.82:LOOP .78:
.86:ListGetNext EDI                              ; The next symbol.
    JNZ .74:
.88: ListGetNext EBX                             ; The next loaded module.
     JNZ .70:
.90: Invoke EaBufferRelease::,[%ExtBuf]
     Invoke EaBufferRelease::,[%PubBuf]
   EndProcedure PgmSelectModules
↑ PgmStoreGlobalSymbols PgmPtr, PubBuffer, ExtBuffer
PgmStoreGlobalSymbols will store all extern|import symbols to ExtBuffer and all public|export symbols to PubBuffer.
Input
PgmPtr is pointer to PGM whose symbols are investigated.
PubBuffer is pointer to a BUFFER reserved by caller.
ExtBuffer is pointer to a BUFFER reserved by caller.
Output
Dword pointers to global symbols are stored to PubBuffer and ExtBuffer.
Error
-
Invoked by
PgmSelectModules
PgmStoreGlobalSymbols Procedure PgmPtr,PubBuffer,ExtBuffer
     MOV EDI,[%PgmPtr]
     MOV ECX,[%PubBuffer]
     MOV EDX,[%ExtBuffer]
     ListGetFirst [EDI+PGM.SymList]
     JZ .90:
.30: JNSt [EAX+SYM.Status],symExtern|symImport,.50:
     BufferStoreDword EDX,EAX
.50: JNSt [EAX+SYM.Status],symPublic|symExport,.80:
     BufferStoreDword ECX,EAX
.80: ListGetNext EAX ; The next symbol.
     JNZ .30:
.90: EndProcedure PgmStoreGlobalSymbols
↑ PgmCreateImportModule BasePgmPtr, DllNamePtr, DllNameSize, OrdinalNr, SymStatus, SymNamePtr, SymNameSize, InterNamePtr, InterNameSize
PgmCreateImportModule will create a new module in PGM format and store the module on BasePgm.ModulePgmList. The module is marked pgmImpModule and it contains only the imported symbol.
Name of the module is SymName. All names may be volatile.
Input
BasePgmPtr is pointer to PGM where is the module created.
DllNamePtr is pointer to the name of DLL which the symbol is imported from.
DllNameSize is the number of bytes in DllName.
OrdinalNr is the ordinal number of the imported symbol.
SymStatus is the SYM.Status of the imported symbol, usually symImport+'A'.
SymNamePtr is pointer to the name of imported symbol.
SymNameSize is the number of bytes in SymName.
SymInterNamePtr is pointer to the internal (decorated) name of imported symbol.
SymInterNameSize is the number of bytes in SymInterName. It may be 0 when SymInterName==SymName.
Output
A new program was added on BasePgm.ModulePgmList.
Error
-
Invokes
SssCreateExtern
Invoked by
PfdllLoadPgm PflibcofLoadImportModule PfomfLoadModule
PgmCreateImportModule Procedure BasePgmPtr, DllNamePtr, DllNameSize, OrdinalNr, \
                                SymStatus, SymNamePtr, SymNameSize, \
                                SymInterNamePtr, SymInterNameSize
    MOV EBX,[%BasePgmPtr]
    MOV EDX,[EBX+PGM.Pool]
    ListNew [EBX+PGM.ModulePgmList],Zeroed=yes
    MOV EDI,EAX
    MOV [EDI+PGM.Pool],EDX     ; Let module's pool parasite on BasePgm.Pool.
    SetSt [EDI+PGM.Status],pgmImpLibMember
    MOV EAX,pgmoptWidthMask
    AND EAX,[EBX+PGM.Pgmopt.Status]
    OR  EAX,pgmoptLibMember
    SetSt [EDI+PGM.Pgmopt.Status],EAX
    ListCreate EDX,SIZE#SSS
    MOV [EDI+PGM.SssList],EAX
    ListCreate EDX,SIZE#SYM
    MOV [EDI+PGM.SymList],EAX
    ListNew EAX,Zeroed=yes
    MOV EBX,EAX ; EBX is now pointer to the imported symbol.
    MOV EAX,[%SymStatus]
    SetSt [EBX+SYM.Status],EAX
    MOV ESI,[%DllNamePtr]
    MOV ECX,[%DllNameSize]
    PoolStore EDX,ESI,ECX
    MOV [EBX+SYM.DllNamePtr],EAX
    MOV [EBX+SYM.DllNameSize],ECX
    MOV ESI,[%SymNamePtr]
    MOV ECX,[%SymNameSize]
    PoolStore EDX,ESI,ECX
    MOV [EDI+PGM.NamePtr],EAX
    MOV [EDI+PGM.NameSize],ECX
    MOV [EBX+SYM.NamePtr],EAX
    MOV [EBX+SYM.NameSize],ECX
    MOV [EBX+SYM.InterNamePtr],EAX
    MOV [EBX+SYM.InterNameSize],ECX
    MOV ESI,[%SymInterNamePtr]
    MOV ECX,[%SymInterNameSize]
    JECXZ .80:
    PoolStore EDX,ESI,ECX
    MOV [EBX+SYM.InterNamePtr],EAX
    MOV [EBX+SYM.InterNameSize],ECX
.80:MOV EAX,[%OrdinalNr]
    MOV [EBX+SYM.OrdinalNr],EAX
    Invoke SssCreateExtern::,EBX,EDI
   EndProcedure PgmCreateImportModule
↑ PgmStreamImage Pgm, Stream

PgmStreamImage stores the emitted contents of all initialized segments to the %Stream in the order specified on Pgm.SegOrdBuffer by PgmOrderSegments.
All segments should be linked by PgmLink and their SSS.BottomFA set.
Segment FileAddress will be used to stuff the stream by 0 bytes (or with NOP if both segments have PURPOSE=CODE).

Input
Pgm Pointer to a completely assembled, combined, linked PGM with resolved relocations.
Stream is pointer to an output STREAM File headers may be pre-stored to the stream.
Output
Stream is filled with the file image.
Error
F9341 Internal error: invalid linking
Invoked by
PfbinCompile PfbootCompile PfcomCompile PfelfCompile PfelfsoCompile PfelfxCompile PfmzCompile
PgmStreamImage Procedure Pgm, Stream
Purpose    LocalVar ; Purpose of the current segment.
SssPtr     LocalVar ; Pointer to the array of pointers to SSS.
SssPtrTop  LocalVar ; Pointer behind the array of pointers to SSS.
    XOR EDX,EDX
    MOV EBX,[%Pgm]
    MOV [%Purpose],EDX
    BufferRetrieve [EBX+PGM.SegOrdBuffer]
    ADD ECX,ESI
    MOV [%SssPtr],ESI
    MOV [%SssPtrTop],ECX
    MOV EBX,[%Stream]
.20:MOV ESI,[%SssPtr]
    CMP ESI,[%SssPtrTop]                    ; ESI points to an array of pointers.
    JNB .90:
    LODSD
    MOV [%SssPtr],ESI
    JNSt [EAX+SSS.Status],sssSegment,.20:
    JNSt [EAX+SSS.Status],sssNotBSS,.20:
.28:                                        ; EAX is an emitting segment (CODE,RODATA,DATA) or special segment (SYMBOLS, STRINGS etc).
      MOV EDI,EAX                           ; EDI is the segment with initialized contents.
      MOV EAX,[EDI+SSS.Purpose]
      XOR EDX,EDX                           ; DL is alignment stuff 0x00 or 0x90.
      XCHG EAX,[%Purpose]
      JNSt EAX,sssPurposeCODE,.30:
      JNSt [%Purpose],sssPurposeCODE,.30:
      MOV DL,0x90                           ; Stuff with NOP when both EDI and the previous segment has purpose CODE.
.30:  StreamGetSize EBX                     ; Load EAX with the number of already streamed bytes.
      MOV ECX,[EDI+SSS.BottomFA]
      SUB ECX,EAX
      JZ .50:
      JB .F9341:                            ; Incorrectly linked program, abort.
.40:  StreamStoreByte EBX,DL                ; Intersection alignment stuff.
      LOOP .40:
.50:  BufferRetrieve [EDI+SSS.EmitBuffer]
      StreamStore [%Stream],ESI,ECX
.80:  JMP .20:                                ; The next segment.
.F9341:Msg '9341',EAX                       ; Internal error: invalid linking at file offset !1Hh.
.90:EndProcedure PgmStreamImage
↑ PgmResizeGroups Prog
PgmResizeGroups recalculates virtual and file size of all groups in the Prog and in its loaded modules, considering all segments of the program which belong to this group.
Group.Bottom will be the lowest bottom and Group.Top the highest top of all its segments.
Group.Bottom will be 0 in program format COM, as the addressing frame respects PSP (256 bytes). Similarily in format BOOT.
Group.BottomFA will be the lowest file bottom and Group.TopFA the highest file top of all its segments (this is used in ELFX format).
PgmResizeGroups 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 purposes of all its segments.
Input
Prog is pointer to PGM where the groups belongs.
Output
Group.Bottom(FA) and Group.Top(FA) are recalculated, .Status and .Purpose updated..
Error
Errors are reported with macro Msg.
Invoked by
PfcoffCompile PfelfLink PfelfsoLink PfelfxLink PfomfStoreModule PgmCombine PgmLink
Invokes
SssResizeGroup
PgmResizeGroups Procedure Prog
    MOV EBX,[%Prog]
    ListGetFirst [EBX+PGM.SssList]          ; Resize groups in the base program.
    JZ .30:
    XOR ECX,ECX
.10:JNSt [EAX+SSS.Status],sssGroup, .20:
    Invoke SssResizeGroup::,EAX,EBX
.20:ListGetNext EAX
    JNZ .10:
.30:ListGetFirst [EBX+PGM.ModulePgmList]    ; Resize groups in the loaded modules.
    JZ .90:
.40:MOV EDI,EAX
    ListGetFirst [EDI+PGM.SssList]
    JZ .70:
.50:JNSt [EAX+SSS.Status],sssGroup, .60:
    Invoke SssResizeGroup::,EAX,EBX
.60:ListGetNext EAX
    JNZ .50:
.70:ListGetNext EDI
    JNZ .40:
.90:EndProcedure PgmResizeGroups
↑ PgmGroupByModel Program

PgmGroupByModel will assign orphaned segment to groups in NEAR memory models.
PgmGroupByModel is invoked when linkable modules have been already combined into this Program.

Input
Program Pointer to PGM whose segments will be regrouped.
Output
Each segment of the Program now belongs either to explicit or to implicitly created group.
Error
-
See also
SssCreateImplicit,
PROGRAM MODEL=
Invoked by
PfbinCompile PfbootCompile PfcomCompile PfmzCompile PfpeCompile
Invokes
SssCreateGroup SssResizeGroup SymCreateSe
PgmGroupByModel Procedure Program
    MOV EBX,[%Program]
    MOV EAX,[EBX+PGM.Pgmopt.Status]
    AND EAX,pgmoptModelMask
    Dispatch EAX,pgmoptFLAT,pgmoptSMALL,pgmoptMEDIUM,pgmoptCOMPACT,pgmoptLARGE,pgmoptHUGE
.pgmopTINY:                                      ; Regroup all orphaned segments to one common group.
    MOV ECX,sssPurposeRegular
    CALL .Regroup:
    JMP .pgmoptFLAT:
.Regroup:PROC1 ; Find or create a group with purpose ECX and assign nongrouped segments with purpose ECX to the group.
               ; Input: EBX=^PGM, ECX=purpose
    ListGetFirst [EBX+PGM.SssList]
    JZ .R9:
    MOV EDX,6                                    ; Size of the new group name [?GROUP].
.R1:JNSt [EAX+SSS.Status],sssGroup,.R2:
    JSt [EAX+SSS.Purpose],ECX,.R5:
.R2:ListGetNext EAX
    JNZ .R1:
    CMP ECX,sssPurposeRegular
    JNE .R3:
    MOV ESI,=B"COMGROUP"
    INC EDX,EDX                                  ; Size of the new group name [COMGROUP].
    JMP .R4:
.R3:Dispatch ECX,sssPurposeCODE,sssPurposeSTACK
    MOV ESI,=B"DGROUP"                            ; sssPurposeDATA|RODATA|BSS.
    JMPS .R4:
.sssPurposeCODE:
    MOV ESI,=B"CGROUP"
    JMPS .R4:
.sssPurposeSTACK:
    MOV ESI,=B"SGROUP"
.R4:Invoke SssCreateGroup::,[EBX+PGM.CurrentStm],ESI,EDX,sssGroup+sssImplicit,ECX
    Invoke SymCreateSe::,EAX,EBX
.R5:MOV EDI,EAX
    ListGetFirst [EBX+PGM.SssList]                ; EDI is the group with the purpose ECX.
.R6:JNSt [EAX+SSS.Status],sssSegment,.R8:         ; Assign all ungrouped segments with the purpose ECX to the group EDI.
    JNSt [EAX+SSS.Purpose],ECX,.R8:
    MOV EDX,[EAX+SSS.GroupPtr]
    TEST EDX
    JNZ .R8:
    MOV [EAX+SSS.GroupPtr],EDI                    ; Assign ungrouped segment EAX to the group EDI.
.R8:ListGetNext EAX
    JNZ .R6:
.R9:RET
   ENDP1 .Regroup:
.pgmoptSMALL:                                    ; All segments are NEAR.
.pgmoptCOMPACT:                                  ; Code segments are NEAR, data segments are FAR.
    MOV ECX,sssPurposeCODE
    CALL .Regroup:
    JSt [EBX+PGM.Pgmopt.Status],pgmoptCOMPACT,.pgmoptLARGE:
.pgmoptMEDIUM:                                   ; Data segments are NEAR, code segments are FAR.
    MOV ECX,sssPurposeDATA+sssPurposeRODATA+sssPurposeBSS
    CALL .Regroup:
.pgmoptLARGE:                                    ; Code and data segments are FAR.
.pgmoptHUGE:
    MOV ECX,sssPurposeSTACK                      ; Stack segments are NEAR in all models.
    CALL .Regroup                                ; Find or create [SGROUP] and assign ungrouped stack segments to it.
.pgmoptFLAT:                                     ; Do not group any segments in FLAT model.
    ListGetFirst [EBX+PGM.SssList]               ; Finally, merge VA, FA, purpose and sssNotBSS status from all group's segments.
    JZ .90:
.50:JNSt [EAX+SSS.Status],sssGroup,.70:
    Invoke SssResizeGroup::,EAX,EBX
.70:ListGetNext EAX
    JNZ .50:
.90:EndProcedure PgmGroupByModel
↑ PgmLinkSections Pgm
PgmLinkSections is invoked at the end of program assembly when ENDPROGRAM is encountered in the last pass. It links sections to their base segments.
Input
Pgm Pointer to PGM.
Output
Each sections is linked to its base segment, its Bottom and Top are adjusted, its emitted contents copied to base segment, relocations fixed up and copied to base segment.
Error
Errors are reported with macro Msg.
Invokes
SssLinkSections SssOrderSections
Invoked by
PseudoENDPROGRAM
PgmLinkSections Procedure Pgm
    MOV EBX,[%Pgm]
    ListGetFirst [EBX+PGM.SssList]
    JZ .60:
.10:JNSt [EAX+SSS.Status],sssSegment,.15:   ; Pass 1: link VA of sections, concatenate contents.
    Invoke SssOrderSections::,EAX,EBX
    Invoke SssLinkSections::, EAX
.15:ListGetNext EAX
    JNZ .10:
    ListGetFirst [EBX+PGM.SssList]          ; Pass 2: update relocations in segments.
.20:JNSt [EAX+SSS.Status],sssSegment,.50:
    BufferRetrieve [EAX+SSS.RelocBuffer]
    ADD ECX,ESI
.30:CMP ESI,ECX
    JNB .50:                                ; When there are no more relocations in segment EAX.
    PUSH ECX
      MOV ECX,[ESI+RELOC.Section]
      MOV EDI,[ECX+SSS.BottomLow]
      MOV EDX,[ECX+SSS.BottomHigh]
      MOV ECX,[ECX+SSS.SegmPtr]
      JECXZ .35:
      SUB EDI,[ECX+SSS.BottomLow]
      SBB EDX,[ECX+SSS.BottomHigh]
      ADD [ESI+RELOC.OrgLow],EDI
      ADC [ESI+RELOC.OrgHigh],EDX
      MOV [ESI+RELOC.Section],ECX
.35:  MOV ECX,[ESI+RELOC.Status]
      JSt ECX,relocIgnore,.40:
      AND ECX,relocTypeMask
      MOV ECX,[ESI+RELOC.Symbol]
      JECXZ .40:
      MOV ECX,[ECX+SYM.Section]
      JECXZ .40:
      MOV EDI,[ECX+SSS.BottomLow]
      MOV EDX,[ECX+SSS.BottomHigh]
      ADD [ESI+RELOC.AddendLow],EDI
      ADC [ESI+RELOC.AddendHigh],EDX
      MOV ECX,[ECX+SSS.SegmPtr]
      JECXZ .40:
      MOV ECX,[ECX+SSS.SymPtr]
      MOV [ESI+RELOC.Symbol],ECX
.40:POP ECX
    ADD ESI,SIZE# RELOC
    JMP .30:
.50:ListGetNext EAX
    JNZ .20:
.60:ListGetFirst [EBX+PGM.SymList]               ; Update symbols.
    JZ .90:
.70:MOV ECX,[EAX+SYM.Section]
    JECXZ .80:
    JNSt [ECX+SSS.Status],sssSection,.80:
    MOV EBX,[ECX+SSS.SegmPtr]
    MOV EDI,[ECX+SSS.BottomLow]
    MOV EDX,[ECX+SSS.BottomHigh]
    ADD [EAX+SYM.OffsetLow],EDI
    ADC [EAX+SYM.OffsetHigh],EDX
    MOV [EAX+SYM.Section],EBX
    JNSt [EAX+SYM.Status],symSe,.80:
    JSt [ECX+SSS.Status],sssSegment|sssGroup,.80:
    RstSt [EAX+SYM.Status],symSe                  ; Make symSe of sssSection an ordinary symPrivate.
.80:ListGetNext EAX
    JNZ .70:
.90:
   EndProcedure PgmLinkSections
↑ PgmSymResolve BasePgm
Combining linked modules often ends with multiple symbols with the same name in BasePgm. PgmSymResolve will match unresolved external, exported and imported symbols with homonymous public or weak symbol which were combined to the BasePgm. Succesfully matched symbol is then marked with PgmSymResolved and its SYM.SymbPtr points to the matching public (or weak) symbol in BasePgm.
PgmSymResolve does not create new symbols.
Input
BasePgm is pointer to a PGM which it is linked to.
Output
Symbols in BasePgm.SymList and symbols refered in relocations RELOC.Symbol are resolved.
Error
-
Invokes
SymResolve
Invoked by
PfOutput PfelfCompile PfelfsoCompile PfpeCompile
PgmSymResolve Procedure BasePgm
RelocEnd LocalVar                 ; End of array of RELOC records.
     MOV EBX,[%BasePgm]
    ; Resolve symbols combined in BasePgm.
    ListGetFirst [EBX+PGM.SymList]
    JZ .90:
.10:MOV EDI,EAX


    mov ecx,[edi+SYM.NamePtr]

.15:Invoke SymResolve::,EDI,EBX       ; tady to mrzne, protoze LIST of SYMs je serazeny dle abecedy a cykli kolem dokola
.20:ListGetNext EDI
    JNZ .10:                                     ; The next symbol in BasePgm.
; Resolve target symbols of relocations combined in BasePgm.
    ListGetFirst [EBX+PGM.SssList]
    JZ .90:
.30:MOV EDX,EAX                                  ; EDX is a segment of BasePgm.
    BufferRetrieve [EDX+SSS.RelocBuffer]
    ADD ECX,ESI
    MOV [%RelocEnd],ECX
.40:CMP ESI,[%RelocEnd]
    JNB .60:                                     ; Skip when there are no (more) relocations in segment EDX.
    JSt [ESI+RELOC.Status],relocResolved,.50:
    MOV EAX,[ESI+RELOC.Symbol]                   ; Resolve relocation target.
    TEST EAX
    JZ .50:
    MOV ECX,[EAX+SYM.SymbPtr]                    ; Is the symbol EAX is redirected to a public ECX?
    JECXZ .50:
    MOV [ESI+RELOC.Symbol],ECX
.50:ADD ESI,SIZE# RELOC                          ; The next RELOC.
    JMP .40:
.60:ListGetNext EDX
    JNZ .30:                                     ; The next segment.
.90:EndProcedure PgmSymResolve
↑ PgmRelocResolve Program
PgmRelocResolve will resolve all relocations in the Program.
It is invoked when an executable program is combined and linked, external symbols resolved and virtual segment addresses fixed in image.
Input
Program is pointer to PGM.
Output
Data or code in SSS.EmitBuffer of all Program segments is modified: emited relocable Word/Dword/Fword pointed to by RELOC.Org is fixed up according to relocation type.
Resolved relocations in SSS.RelocBuffer are then marked as relocResolved.
Error
-
Invoked by
PfOutput PfbinCompile PfbootCompile PfcomCompile PfelfxCompile PfmzCompile PfpeCompile
Tested by
t1642 t1643 t1644 t1647 t1648
Invokes
SssRelocResolve
PgmRelocResolve Procedure Program
     MOV EBX,[%Program]
     ListGetFirst [EBX+PGM.SssList]
     JZ .90:
.10: JNSt [EAX+SSS.Status],sssSegment,.80:
     Invoke SssRelocResolve::,EAX,EBX            ; Resolve all relocation in segment EAX.
.80: ListGetNext EAX
     JNZ .10:                                    ; The next segment.
.90:EndProcedure PgmRelocResolve
PgmLink Procedure PgmPtr, FAbottom, VAbottom
ArrayPtr     LocalVar ; Pointer to the array of pointers to SSS segments.
ArrayEnd     LocalVar ; Behind the last pointer.
Size         LocalVar ; Virtual (emitted or reserved) segment size.
FileAlign    LocalVar ; Special ELF section Alignment or the greater of %^FileAlign and regular segment's Alignment.
SectAlign    LocalVar ; Greater of %^SectionAlign and segment Alignment.
VALow        LocalVar ; Segment virtual address when linked,
VAHigh       LocalVar ;   including ImageBase.
AssumedCS    LocalVar ; SVA of code segment  (relative to the assumed CS).
AssumedDS    LocalVar ; SVA of data segment  (relative to the assumed DS).
AssumedSS    LocalVar ; SVA of stack segment (relative to the assumed SS).
    MOV EBX,[%PgmPtr]
    MOV ESI,[%FAbottom]                          ; Initialize file~ and virtual addresses.
    XOR EDI,EDI
    Invoke ExpAlign::,ESI,[EBX+PGM.Pgmopt.FileAlign],EDI
    ADD [%FAbottom],ECX                          ; Alignment stuff following the headers.
    MOV ESI,[%VAbottom]
    MOV EAX,[EBX+PGM.Pgmopt.ImageBaseLow]
    MOV EDX,[EBX+PGM.Pgmopt.ImageBaseHigh]
    ADD EAX,ESI
    ADC EDX,EDI
    Invoke ExpAlign::,EAX,[EBX+PGM.Pgmopt.SectionAlign],EDI
    ADD EAX,ECX                                  ; Alignment stuff following the headers.
    ADC EDX,EDI
    MOV [%VALow],EAX
    MOV [%VAHigh],EDX
    MOV [%AssumedCS],EAX
    MOV [%AssumedDS],EAX
    MOV [%AssumedSS],EAX
    BufferRetrieve [EBX+PGM.SegOrdBuffer]
    ADD ECX,ESI
    MOV [%ArrayPtr],ESI
    MOV [%ArrayEnd],ECX
.25:MOV ESI,[%ArrayPtr]                          ; The main loop: Link FA, SVA and VA of all segments.
    CMP ESI,[%ArrayEnd]
    JNB .80:
    LODSD
    MOV [%ArrayPtr],ESI
    JNSt [EAX+SSS.Status],sssSegment,.25:        ; Omit groups and external pseudosegments.
    CMPD [EAX+SSS.NameSize],0                    ; Omit the empty section [].
    JZ .25:
    MOV EDI,EAX                                  ; Link segment EDI.
    JSt [EBX+PGM.Pgmopt.Status],pgmoptLinkable,.28:
    SetSt [EDI+SSS.Status],sssLinked
.28:JSt [EDI+SSS.Purpose],sssPurposePHDR,.25:    ; Do not elevate VA os [PSP].
    MOV EDX,[EDI+SSS.Alignment]                  ; EDX is segment's own alignment.
    MOV ECX,[EBX+PGM.Pgmopt.SectionAlign]        ; Compute effective alignment of VA..
    CMP ECX,EDX
    JAE .30:
    MOV ECX,EDX
.30:MOV [%SectAlign],ECX
    JNSt [EDI+SSS.Purpose],sssPurposeRegular|sssPurposeOptionalMask,.34:
    MOV ECX,[EBX+PGM.Pgmopt.FileAlign]           ; Compute effective alignment of FA..
    CMP ECX,EDX
    JAE .35:
.34:MOV ECX,EDX
.35:MOV [%FileAlign],ECX
    MOV EAX,[EDI+SSS.TopLow]                     ; Compute segment size in memory.
    MOV EDX,[EDI+SSS.TopHigh]
    SUB EAX,[EDI+SSS.BottomLow]
    SBB EDX,[EDI+SSS.BottomHigh]
    Msg cc=NZ,'8525',EDI                         ; Size of segment [!1S] exceeded 4 GB.
    MOV [%Size],EAX
    MOV ESI,EAX
    MOV EDX,[%FAbottom]                          ; Unaligned FA of the previous segment.
    Invoke ExpAlign::,EDX,[%FileAlign],0         ; Compute segment's file address.
    ADD ECX,EDX                                  ; Alignment stuff following the headers|previous segment contents.
    MOV [EDI+SSS.BottomFA],ECX                   ; Aligned FA of this segment.
    JSt [EDI+SSS.Status],sssImagePrefix,.40:     ; Skip when EDI is COM|BOOT pseudosegment (PSP).
    JNSt [EDI+SSS.Status],sssNotBSS,.40:         ; Skip when ESI is BSS segment.
    ADD ECX,ESI                                  ; Add %Size in the file.
.40:MOV [EDI+SSS.TopFA],ECX
    MOV [%FAbottom],ECX                          ; Unaligned FA of the next segment.
    JSt [EBX+PGM.Pgmopt.Status],pgmoptLinkable,.43:
    MOV EAX,[%VALow]                             ; Compute segment's absolute virtual address.
    MOV EDX,[%VAHigh]                            ; EDX:EAX is unaligned VA of the previous segment.
    Invoke ExpAlign::,EAX,[%SectAlign],0         ; Align the virtual top of previous segment.
    ADD EAX,ECX                                  ; Add returned stuff size.
    ADC EDX,0
    JNSt [EBX+PGM.Pgmopt.Status],pgmoptELFbased,.42:
    MOV ECX,[EBX+PGM.Pgmopt.SectionAlign]        ; Make ELF section VA and FA congruent modulo %^SectionAlign.
    DEC ECX
    AND ECX,[EDI+SSS.BottomFA]
    ADD EAX,ECX
    ADC EDX,0
.42:MOV [EDI+SSS.BottomLow],EAX
    MOV [EDI+SSS.BottomHigh],EDX
    ADD EAX,[%Size]
    ADC EDX,0
    MOV [EDI+SSS.TopLow],EAX
    MOV [EDI+SSS.TopHigh],EDX
    MOV [%VALow],EAX
    MOV [%VAHigh],EDX                            ; Unaligned VA of the next segment.
.43:MOV ECX,[EDI+SSS.GroupPtr]                   ; Compute segment relative offset to its frame group.
    MOV EAX,[EDI+SSS.BottomLow]
    MOV EDX,[EDI+SSS.BottomHigh]
    JECXZ .55:
    CMP EDX,[ECX+SSS.BottomHigh]
    JB .45:
    JA .50:
    CMP EAX,[ECX+SSS.BottomLow]
    JAE .50:
.45:MOV [ECX+SSS.BottomLow],EAX                  ; Group bottom is the lowest of all its segments.
    MOV [ECX+SSS.BottomHigh],EDX
.50:SUB EAX,[ECX+SSS.BottomLow]
    SBB EDX,[ECX+SSS.BottomHigh]
    Msg cc=NZ,'8515',EDI                         ; Segment [!1S] over 4 GB.
    MOV [EDI+SSS.SVA],EAX
.55:JMP .25:
.80:Invoke PgmResizeGroups,EBX                   ; Update VA of groups by their segments.
.90:EndProcedure PgmLink
↑ PgmOrderSegments Pgm

PgmOrderSegments decides about group and segment ordering in output program file. Pointers to SSS objects will be stored to Pgm.SegOrdBuffer in the desired order. Each SSS object is counted exactly once.
Group membership of all segments have already been established.
Ordinal number of each segment in PGM.SegOrdBuffer will be written to SSS.SegmIndex.

The order depends on SSS properties: access rights (read|write|execute), purpose, initialization status (sssNotBSS), alignment (descending).
Though many groups and segments might not exist, ordering rules are common for all program formats supported by €ASM except for ELF-based formats, which are ordered in Pfelf*Compile.

  1. Ungrouped code segments
  2. Grouped Image prefix segment [PSP]
  3. Grouped code segments
  4. Ungrouped read-only data segments
  5. Grouped read-only data segments
  6. Ungrouped data segments
  7. Grouped data segments
  8. Other regular initialized segments
  9. Ungrouped regular uninitialized segments
  10. Grouped regular uninitialized segments
  11. Ungrouped stack segments
  12. Grouped stack segments
  13. Other uninitialized segments

Group and segment order will be visible in ListMap displayed in listing.

Input
Pgm points to PGM - program whose segments are being sorted. Virtual address of each segment is 0.
Output
Pgm.SegOrdBuffer is filled with DWORD pointers to SSS objects (groups and segments) in their final order.
Number of stored groups and segments with initialized code or data (sssNotBSS) is written to Pgm.NrOfInitSegments. If there are more pointers in Pgm.SegOrdBuffer than Pgm.NrOfInitSegments, they all are uninitialized and their raw data may be omitted from image.
Manual
Ordering of segments
Invoked by
PfbinCompile PfbootCompile PfcoffCompile PfcomCompile PfmzCompile PfomfStoreModule PfpeCompile
Tested by
t7901 t7904
PgmOrderSegments Procedure Pgm
SegmIndex    LocalVar                               ; SSS.SegmIndex (ordinal number in SHDR table).
ArrayPtr     LocalVar ; Pointer to the array of DWORD pointers to SSS segments.
ArrayEnd     LocalVar ; Behind the last pointer.
Ptr1         LocalVar ; Pointer to the 1st pointer of subarray.
    MOV EBX,[%Pgm]
    MOV ECX,[EBX+PGM.SegOrdBuffer]
    JECXZ .10:
    BufferClear ECX
    JMPS .15:
.10:BufferCreate [EBX+PGM.Pool],Size=128         ; Create PGM.SegOrdBuffer if it wasn't initialized.
    MOV [EBX+PGM.SegOrdBuffer],EAX
.15:; Pass 1 - prepare flags.
    MOVD [%SegmIndex],-1                         ; Segment index is 0-based.
    ListGetFirst [EBX+PGM.SssList]
    JZ .99:
.20:RstSt [EAX+SSS.Status],sssOrdered+sssUsed    ; Reset flag sssOrdered which guarantees that segments&groups are ordered just once
.25:ListGetNext EAX                              ;  and the groups (except for [PHDR] are not empty.
    JNZ .20:
.30:; Pass 2 - examine used (nonempty) groups.
    ListGetFirst [EBX+PGM.SssList]
.35:JNSt [EAX+SSS.Status],sssSegment,.40:
    MOV ECX,[EAX+SSS.GroupPtr]
    JECXZ .40:
    SetSt [ECX+SSS.Status],sssUsed               ; Mark the group ECX of segment EAX as used.
    MOV EDX,[EAX+SSS.Purpose]                    ; Update purposes of each group.
    OR [ECX+SSS.Purpose],EDX                     ; Group ECX inherits each purpose of its segments.
    JNSt [EAX+SSS.Status],sssNotBSS,.40:
    SetSt [ECX+SSS.Status],sssNotBSS             ; If segment EAX is initialized, group ECX is too.
.40:ListGetNext EAX
    JNZ .35:
.45:; Pass 3 - remove unused (empty) groups.
    ListGetFirst [EBX+PGM.SssList]
.50:JNSt [EAX+SSS.Status],sssGroup,.55:
    JSt [EAX+SSS.Status],sssUsed,.55:
    ListRemove [EBX+PGM.SssList],EAX             ; Remove unused group.
    JMP .45:                                     ; Start the list again.
.55:ListGetNext EAX
    JNZ .50:
.57:
    ; Step 4 - the actual ordering employs macros OrderSegment and OrderGroup.

OrderGroup %MACRO Purpose=sssPurposeAny, NamePtr=0, NameSize=0
    %IF "%Purpose" !== "EDX"
      MOV EDX,%Purpose
    %ENDIF
    MOV ESI,%NamePtr
    MOV ECX,%NameSize
    CALL PgmOrderSegments.OrderGroup:
   %ENDMACRO OrderGroup


OrderSegment %MACRO Purpose=sssPurposeAny, NamePtr=0, NameSize=0, Group=-1
    %IF "%Purpose" !== "EDX"
      MOV EDX,%Purpose
    %ENDIF
    MOV ESI,%NamePtr
    MOV ECX,%NameSize
    %IF "%Group" !== "EDI"
      MOV EDI,%Group
    %ENDIF
    CALL PgmOrderSegments.OrderSegment:
   %ENDMACRO OrderSegment

    ; The actual group&segment order.

.G1:OrderGroup NamePtr= =B'COMGROUP',NameSize=8
    JZ .C5:
    MOV EDI,EAX
.G3:OrderSegment Purpose=sssPurposePHDR,Group=EDI  ; Pseudosegment [PSP] (used in COM).
.G4:OrderSegment Purpose=sssPurposeCODE,Group=EDI
    JNZ .G4:
.G5:OrderSegment Purpose=sssPurposeRODATA,Group=EDI
    JNZ .G5:
.G6:OrderSegment Purpose=sssPurposeDATA,Group=EDI
    JNZ .G6:
.G7:OrderSegment Purpose=sssPurposeBSS,Group=EDI
    JNZ .G7:
.G8:OrderSegment Purpose=sssPurposeSTACK,Group=EDI
    JNZ .G8:

.C5:OrderGroup Purpose=sssPurposeCODE
    JZ .C9:
    MOV EDI,EAX
.C6:OrderSegment Purpose=sssPurposeCODE,Group=EDI
    JNZ .C6:
    JMP .C5:                                       ; The next code group.
.C9:OrderSegment Purpose=sssPurposeCODE            ; Ungrouped code segments.
    JNZ .C9:

.R5:OrderGroup Purpose=sssPurposeRODATA            ; Monodata memory models may group rodada, data and bss segments.
    JZ .R9:
    MOV EDI,EAX
.R6:OrderSegment Purpose=sssPurposeRODATA,Group=EDI
    JNZ .R6:
.R7:OrderSegment Purpose=sssPurposeDATA,Group=EDI
    JNZ .R7:
.R8:OrderSegment Purpose=sssPurposeBSS,Group=EDI
    JNZ .R8:
    JMP .R5:                                       ; The next rodata group.
.R9:OrderSegment Purpose=sssPurposeRODATA         ; Ungrouped rodata segments.
    JNZ .R9:

.D5:OrderGroup Purpose=sssPurposeDATA
    JZ .D9:
    MOV EDI,EAX
.D6:OrderSegment Purpose=sssPurposeRODATA,Group=EDI    ; Monodata memory models may group rodada, data and bss segments.
    JNZ .D6:
.D7:OrderSegment Purpose=sssPurposeDATA,Group=EDI
    JNZ .D7:
.D8:OrderSegment Purpose=sssPurposeBSS,Group=EDI
    JNZ .D8:
    JMP .D5:                                        ; The next data group.
.D9:OrderSegment Purpose=sssPurposeDATA          ; Ungrouped data segments.
    JNZ .D9:

.B5:OrderGroup Purpose=sssPurposeBSS
    JZ .B9:
    MOV EDI,EAX
.B6:OrderSegment Purpose=sssPurposeRODATA,Group=EDI    ; Monodata memory models may group rodada, data and bss segments.
    JNZ .B6:
.B7:OrderSegment Purpose=sssPurposeDATA,Group=EDI
    JNZ .B7:
.B8:OrderSegment Purpose=sssPurposeBSS,Group=EDI
    JNZ .B8:
    JMP .B5:                                        ; The next bss group.
.B9:OrderSegment Purpose=sssPurposeBSS              ; Ungrouped bss segments.
    JNZ .B9:

.S1:OrderGroup Purpose=sssPurposeSTACK
    JZ .S5:
    MOV EDI,EAX
.S2:OrderSegment Purpose=EDX,Group=EDI
    JNZ .S2:
    JMP .S1:                                      ; The next stack group.
.S5:OrderSegment Purpose=sssPurposeSTACK          ; Ungrouped stack segments.
    JNZ .S5:

    OrderSegment    sssPurposeEXPORT               ; Order PE-special segment [.edata].
    OrderSegment    sssPurposeIMPORT               ; Order PE-special segment [.idata].
    OrderSegment    sssPurposeRESOURCE             ; Order PE-special segment [.rsrc].
    OrderSegment    sssPurposeBASERELOC            ; Order PE-special segment [.reloc].
    OrderSegment                                  ; Any other remaining segments.

    ; Alphabetically reorder segments from the same group with identical purposes.
    BufferRetrieve [EBX+PGM.SegOrdBuffer]
    ADD ECX,ESI                                     ; ESI..ECX is an array of DWORD pointers to segments (perhaps in wrong order).
    MOV [%ArrayPtr],ESI
    MOV [%ArrayEnd],ECX
.60:MOV ESI,[%ArrayPtr]                             ; Find a group.
    CMP ESI,[%ArrayEnd]
    JNB .99:
    LODSD
    MOV [%ArrayPtr],ESI
.65:JNSt [EAX+SSS.Status],sssGroup,.60:
    MOV EBX,EAX                                     ; EBX is pointer to a group. Pointers to its segments may follow.
.70:MOV ESI,[%ArrayPtr]
    CMP ESI,[%ArrayEnd]
    JNB .60:
    MOV [%Ptr1],ESI
    LODSD
    MOV [%ArrayPtr],ESI
    JNSt [EAX+SSS.Status],sssSegment,.65:
    CMP [EAX+SSS.GroupPtr],EBX
    JNE .60:
    MOV EDX,[EAX+SSS.Purpose]
.75:                                                 ; Find more segments with identical group EBX and purpose EDX.
    MOV ESI,[%ArrayPtr]
    CMP ESI,[%ArrayEnd]
    JNB .85:
    LODSD
    MOV [%ArrayPtr],ESI
    JNSt [EAX+SSS.Status],sssSegment,.80:
    CMP [EAX+SSS.GroupPtr],EBX
    JNE .80:
    CMP [EAX+SSS.Purpose],EDX
    JE .75:
.80:MOV ESI,[%ArrayPtr]                             ; The just loaded object at %ArrayPtr is not a suitable segment. Roll back.
    SUB ESI,4
    MOV [%ArrayPtr],ESI
.85:MOV EDX,[%ArrayPtr]
    SUB EDX,[%Ptr1]                                 ; Array [%Ptr1] .. [%ArrayPtr] contains pointers to groupen segments with identical purpose.
    CMP EDX,4
    JNA .70:                                        ; Skip when nothing to sort.
    SHR EDX,2                                       ; EDX is number of segments to sort.
    ; If at least one segment has $ in its name, sort all of them alphabetically.
    MOV ESI,[%Ptr1]
.90:CMP ESI,[%ArrayPtr]
    JNB .70:
    LODSD
    MOV EDI,[EAX+SSS.NamePtr]
    MOV ECX,[EAX+SSS.NameSize]
    MOV AL,'$'
    REPNE SCASB
    JNE .90:                                     ; No '$' in segment name, try the next segment.
    ShellSort [%Ptr1],EDX,4,.ByName
    JMP .70:

.ByName: PROC                                    ; Sort callback procedure. Input: ESI and EDI points to DWORD ^SSS.
    PUSH ECX,EBX,EBP
      MOV EBX,[ESI]
      MOV EBP,[EDI]
      MOV ECX,[EBX+SSS.NameSize]
      MOV EDX,[EBP+SSS.NameSize]
      CMP ECX,EDX
      JBE .N2:
      MOV ECX,EDX
.N2:  PUSH ESI,EDI
        MOV ESI,[EBX+SSS.NamePtr]
        MOV EDI,[EBP+SSS.NamePtr]
        REPE CMPSB                                  ; Compare shorter part of segment names.
      POP EDI,ESI
      JA .Swap:
      JB .Ord:
      CMP EDX,[EBP+SSS.NameSize]                    ; When they match, compare lengths.
      JB  .Swap:
.Ord: CLC                                           ; Both segments EBX,EBP are in order.
      JMPS .N9:
.Swap:MOV ECX,[EBX+SSS.SegmIndex]                   ; Swap pointers at ESI,EDI and their segment indexes.
      MOV EDX,[EBP+SSS.SegmIndex]
      MOV [EBX+SSS.SegmIndex],EDX
      MOV [EBP+SSS.SegmIndex],ECX
      MOV [ESI],EBP
      MOV [EDI],EBX
      STC                                            ; Signalize that they were swapped.
.N9:POP EBP,EBX,ECX
    RET
   ENDP .ByName:


.OrderGroup: PROC1                               ; Runtime procedure of macro OrderGroup.
    ; Input: EDX=purpose, ESI=^name, ECX=SIZE# name, EBX=^PGM.
    ; Output: ZF=0, EAX=^SSS, EAX stored to PGM.SegOrdBuffer.
    ; Error:  ZF=1, EAX=0 when the group was not found.
    ListGetFirst [EBX+PGM.SssList]
.G1:JSt [EAX+SSS.Status],sssOrdered,.G7:
    JNSt [EAX+SSS.Status],sssGroup,.G7:
    JNSt [EAX+SSS.Purpose],EDX,.G7:
    TEST ESI                                      ; ESI=0 allows any name.
    JZ .G8:
    CMP ECX,[EAX+SSS.NameSize]
    JNE .G7:
    JECXZ .G8:
    PUSH ECX,ESI,EDI
      MOV EDI,[EAX+SSS.NamePtr]
      REPE CMPSB
    POP EDI,ESI,ECX
    JE .G8:
.G7:ListGetNext EAX                               ; Try the next group.
    JNZ .G1:
    RET                                           ; ZF=1, EAX=0, not found.
.G8:BufferStoreDword [EBX+PGM.SegOrdBuffer],EAX   ; Store group EAX.
    SetSt [EAX+SSS.Status],sssOrdered
    RET                                           ; ZF=0, EAX=^SSS, found.
   ENDP1 .OrderGroup

.OrderSegment: PROC1                              ; Runtime procedure of macro OrderSegment.
    ; Input: EDX=purpose, ESI=^name, ECX=SIZE# name, EDI=^SSS group, EBX=^PGM.
    ; Output: ZF=0, EAX=^SSS, EAX stored to PGM.SegOrdBuffer.
    ; Error:  ZF=1, EAX=0 when the segment was not found.
    ListGetFirst [EBX+PGM.SssList]
.S1:JSt [EAX+SSS.Status],sssOrdered,.S7:
    JNSt [EAX+SSS.Status],sssSegment,.S7:
    JNSt [EAX+SSS.Purpose],EDX,.S7:
    CMP EDI,-1                                    ; Any group membership?
    JE .S5:
    CMP [EAX+SSS.GroupPtr],EDI
    JNE .S7:
.S5:TEST ESI                                      ; ESI=0 allows any name.
    JZ .S8:
    CMP ECX,[EAX+SSS.NameSize]
    JNE .S7:
    JECXZ .S8:
    PUSH ECX,ESI,EDI
      MOV EDI,[EAX+SSS.NamePtr]
      REPE CMPSB
    POP EDI,ESI,ECX
    JE .S8:
.S7:ListGetNext EAX                               ; Try the next segment.
    JNZ .S1:
    RET                                           ; ZF=1, EAX=0, not found.
.S8:BufferStoreDword [EBX+PGM.SegOrdBuffer],EAX   ; Order segment EAX.
    SetSt [EAX+SSS.Status],sssOrdered
    INCD [%SegmIndex]
    PUSHD [%SegmIndex]
    POPD [EAX+SSS.SegmIndex]
    TEST EAX
    RET                                           ; ZF=0, EAX=^SSS, found.
    ENDP1 .OrderSegment:

.99:EndProcedure PgmOrderSegments
   ENDPROGRAM pgm

▲Back to the top▲