EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

pgm.htm
Class
PGM
Encodings
PgmEnc
Procedures
PgmCheckDirty
PgmCheckGlobal
PgmCombine
PgmConcatenateImage
PgmCreateProgram
PgmCreateImplicitGroups
PgmCreateImplicitSegments
PgmCreateImportModule
PgmDestroy
PgmDetectImportModule
PgmEvalEntry
PgmGetCurrent
PgmGlobalEntry
PgmLinkImage
PgmLinkSections
PgmListGlobals
PgmListLiterals
PgmListMap
PgmParameters
PgmSelectModules
PgmOrderSegments
PgmOrderSegmDo
PgmStoreGlobalSymbols

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.


pgm PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
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 PgmCreateImplicitSegments PassCreate PseudoENDPROGRAM ; Parse pseudoinstruction ENDPROGRAM. PgmCheckDirty ; Detect if something was emitted in this program. PgmLinkSections ; Link virtual adress of each section SssLinkSegment ; to its segment. SssLinkSection 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 SssLinkSegment ; to its segment. SssLinkSection PgmListMap ; Append info to the listing. PgmListGlobals ; Append info to the listing. CtxDiscard CtxDestroy continue below ENDPROGRAM statement
PGM STRUC
.NamePtr            D D ; Pointer to the program name.
.NameSize           D D ; Size of the program name.
.PassNr             D D ; Pass number.
.PassPtr            D D ; Pointer to PASS or 0 when no pass is initialized.
                        ; +10h.
.Status             D D ; Program Boolean flags in PgmEncoding.
.CurrentSect        D D ; Pointer to SSS with .Status:sssSection - a section which Pgm emits to.
.CurrentStm         D D ; Pointer to STM with the last parsed statement.
.LinePtr            D D ; Pointer to physical line with current statement of this PROGRAM.
                        ; +20h.
.SymList            D D ; Pointer to LIST with program symbols.
.SssList            D D ; Pointer to LIST with groups, segments, sections and structures.
.SssPtrBuf          D D ; Pointer to BUFFER with DWORD pointers to SSS in image order.
.ModulePgmList      D D ; Pointer to LIST of PGM modules loaded from linked/imported files.
                        ; +30h.
; Synchronous tables describing files included in the program.
.InclFilesNr        D D ; Number of DWORDS in .InclFilesTable,.InclFileTimeTable,.InclLinePtrTable.
.InclFilesTable     D D ; Pointer to the table of DWORDs, each contains pointer to FILE structure.
.InclLinePtrTable   D D ; Pointer to the table of DWORDs, each contains LinePtr to INCLUDE* statement.
.InclFileTimeTable  D D ; Pointer to the table of DWORDs, each contains DosDateTime of included file.
                        ; +40h.
; Synchronous tables describing files linked in the program.
.MaxLinks           D D ; Copy of EUROASM MAXLINKS= option which was in charge on PgmCreateProgram.
.LinkFilesNr        D D ; Number of DWORDS in .LinkFileNamesTable and .LinkLinePtrTable.
.LinkFileNamesTable D D ; Pointer to the table of DWORDs, each contains pointer to ASCIIZ filename.
.LinkLinePtrTable   D D ; Pointer to the table of DWORDs, each contains LinePtr to LINK statement.
                        ; +50h.
.Errorlevel         D D ; Errorlevel of this program.
.NrOfInitSegments   D D ; How many groups+segments in image are initialized (trailing BSS and STACK do not count).
.Pool               D D ; Program pool.
.EntryExp       DS EXP    ; Program entry evaluated to EXP structure by PgmEvalEntry.
.Pgmopt         DS PGMOPT ; Program options of this program.
.Eaopt          DS EAOPT  ; EUROASM options valid at the start of this program.
 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.
pgmNoMsg        = 0x0100_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.
pgmFixingPass   = 0x0000_0080 ; Pass number last but one (MAXPASSES-1).
; Link flags.
pgmLinking      = 0x0000_0100 ; Program was finally assembled, now it is linked.
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.
  ENDHEAD pgm
[.text]
↑ PgmCreateProgram StmPtr
PgmCreateProgram allocates PGM object on Src.Pool and initializes program structures. 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 ; Estimate max.16 segments in the program.
     MOV [EDI+PGM.SssPtrBuf],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::+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 does the final job when last pass has ended. 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:
      ; Envelope program 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 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            ; 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.
      Invoke PgmCheckDirty,EBX         ; Returns CF=1 if something was emitted or linked to the program.
      SBB EDI,EDI                      ; EDI is now -1 if program dirty, 0 if empty.
      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 segments and global symbols.
 .70: JNSt [EBX+PGM.Pgmopt.Status],pgmoptLISTGLOBALS,.80:
      Invoke PgmListGlobals,EBX,[%StmPtr]
 .80: PoolDestroy [EBX+PGM.Pool]
      Msg cc=C,'2575','Pgm',EDX        ; Deallocation of virtual memory !1C.Pool !2Hh failed.
 .90:EndProcedure PgmDestroy
↑ 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 SssFind StmParse SymCreate 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 SssFind StmClean StmCreate StmDestroy StmListing
Invoked by
PseudoENDPROGRAM
PgmListLiterals Procedure Pgm
LSFound  LocalVar ; Boolean flag. Nonzero if subproc .ListSection found the literal section.
SnPtr    LocalVar ; Pointer to a symbol.
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 ; Fake statement used to feed StmListing.
SssName  LocalVar Size=8 ; Room for section name, "@LT##"
    LEA EDI,[%SssName]
    MOV EAX,"@LT#"
    STOSD
    XOR EAX,EAX
    STOSD
    MOV [%@RTnum],EAX
    Invoke EaBufferReserve::,PgmListLiterals
    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 found, try the next one.
     Invoke StmDestroy::,EBX
    POPD [Src.Lst.Status::]
    Invoke EaBufferRelease::,[%SnBuffer]
    JMP .99:

.ListSection: PROC ; List literals from one section.
     ; Input: EAX=section name without '@', e.g. "LT8".
     ;        EBX=Pointer to %Stm.
     ; Output:EBX,EBP unchanged, other register undefined.
     MOVD [%LSFound],0
     LEA EDI,[%SssName]
     MOV ECX,4 ; Section name size.
     MOV [EDI+1],EAX
     TEST EAX,0xFF00_0000
     JZ .S1:
     INC ECX
 .S1:Invoke SssFind::,sssSection,0,EDI,ECX,0
     JC .S9:
     JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.S9:
     MOV EDI,EAX ; Literal section found.
     MOV [%LSFound],EAX
     BufferClear [%SnBuffer]
     MOV EDX,[%Pgm]
     MOV [EDX+PGM.CurrentSect],EDI
     ; Find all literal symbols which belong to section EDI.
     ListGetFirst [EDX+PGM.SymList]
     JZ .S4:
 .S2:; EAX is now pointer to a symbol from program's symbol list.
     JNSt [EAX+SYM.Status],symLiteral,.S3: ; If not a literal symbol.
     CMP [EAX+SYM.Section],EDI
     JNE .S3: ; If the symbol does not belong to listed section.
     MOV [%SnPtr],EAX
     LEA ECX,[%SnPtr]
     BufferStore [%SnBuffer],ECX,4
 .S3:ListGetNext EAX ; Get the next symbol.
     JNZ .S2:
 .S4:BufferRetrieve [%SnBuffer]
     SAR ECX,2 ; ECX is now number of symbols.
     JECXZ .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 looked weird.
     ShellSort ESI,ECX,4,.ByOffset:
.ByOffset: PROC1 ; Callback sorting procedure.
     PUSH EBX
      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: ; If in ascending order.
      MOV [ESI],EDX ; Otherwise swap both elements.
      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
     MOV EAX,[EDX+SYM.Size]
     MOV [EBX+STM.Size],EAX
     MOV EAX,[EDX+SYM.OffsetLow]
     MOV [EBX+STM.OffsetLow],EAX
     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:
 .S7:Invoke StmListing::,EBX
 .S8:POP ESI,ECX
     LOOP .S5:
 .S9:RET
     ENDP .ListSection:
.99: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
EaBufferRelease EaBufferReserve SssPurposeToText SymFrameAddress
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 "',23
      BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt.OutFilePtr],[EBX+PGM.Pgmopt.OutFileSize]
      BufferStore [ESI+STM.MsgBuffer],=B'",groups=',9
      ; Count linked groups and segments.
      SUB EDI,EDI ; Group counter.
      SUB EDX,EDX ; Segment counter.
      BufferRetrieve [EBX+PGM.SssPtrBuf]
      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=',10
      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=',7
      ; List entry=.
      LEA ESI,[EBX+PGM.EntryExp]
      LEA EDI,[%Number]
      MOV EAX,[ESI+EXP.Status]
      MOV ECX,[ESI+EXP.Seg]
      MOV EDX,EDI
      Dispatch AL,'A','N','F'
      JMP .34: ; Ignore wrong entry expression.
.F:   MOV EAX,ECX ; Para value.
      StoH EDI,Size=4
      BufferStore [%MsgBuffer],EDX,4
      BufferStore [%MsgBuffer],=B'h:',2
      MOV EAX,[ESI+EXP.Low]
      MOV EDI,EDX
      StoH EDI,Size=8
      BufferStore [%MsgBuffer],EDX,8
      BufferStore [%MsgBuffer],=B'h',1
      JMP .34:
.25:  MOV ECX,[ESI+EXP.Seg]
      JMP .28:
.N:
.A:   BufferStore [%MsgBuffer],=B'[',1
      MOV ECX,[ESI+EXP.Sym]
      MOV EAX,[ESI+EXP.Low]
      JECXZ .25:
      Invoke SymFrameAddress::,ECX,[%Pgm] ; Return EDX:EAX=offset, ECX=group.
.28:  JECXZ .31:
      BufferStore  [%MsgBuffer],[ECX+SSS.NamePtr],[ECX+SSS.NameSize]
.31:  BufferStore  [%MsgBuffer],=B']:',2
      MOV EDX,EDI
      StoH EDI,Size=8
      BufferStore  [%MsgBuffer],EDX,8
      BufferStore  [%MsgBuffer],=B'h',1
 .34: ; List stack=.
      BufferStore  [%MsgBuffer],=B',stack=',7
      ; Find the last STACK segment to EDX.
      MOV EAX,[%Pgm]
      XOR EDX,EDX
      BufferRetrieve [EAX+PGM.SssPtrBuf]
      SHR ECX,2
      JECXZ .49:
 .37: LODSD
      JNSt [EAX+SSS.Purpose],sssPurposeSTACK,.40:
      MOV EDX,EAX
 .40: LOOP .37:
      TEST EDX
      JZ .49: ; If no stack.
      BufferStore [%MsgBuffer],='[',1
      BufferStore [%MsgBuffer],[EDX+SSS.NamePtr],[EDX+SSS.NameSize]
      BufferStore [%MsgBuffer],=']:',2
      LEA ESI,[EBX+PGM.Pgmopt]
      MOV EAX,[ESI+PGMOPT.Status]
      Dispatch AL,%PfPgmoptList
.pgmoptRSRC:
.pgmoptPE:
.pgmoptDLL:
.pgmoptCOFF:
      MOV EAX,[ESI+PGMOPT.SizeOfStackCommitLow]
      MOV EDX,[ESI+PGMOPT.SizeOfStackCommitHigh]
      JMP .43:
.pgmoptLIBOMF:
.pgmoptLIBCOF:
.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]
      JMP .43:
.pgmoptBIN:
.pgmoptCOM: ; Stack pointer is fixed to the end of [COM] segment.
      MOV EAX,0xFFFE
      SUB EDX,EDX
 .43: 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: ; Terminate ListMap header line.
      BufferStoreWord [%MsgBuffer],0x0A0D

      ; List groups and segments, one per line.
      BufferRetrieve [EBX+PGM.SssPtrBuf]
      SHR ECX,2
      JZ .90:
 .52: LODSD  ; A loop .52: .. .85: displays all ECX groups and their segments.
 .55: PUSH ECX,ESI
        MOV ESI,EAX ; ^SSS.
        BufferStore [%MsgBuffer],=B'|        ',9
        JNSt [ESI+SSS.Status],sssSegment,.58:
        BufferStoreWord [%MsgBuffer],'  '  ; Indent segment by two spaces.
 .58:   BufferStoreByte [%MsgBuffer],'['
        BufferStore [%MsgBuffer],[ESI+SSS.NamePtr],[ESI+SSS.NameSize]
        MOV EBX,[%Pgm]
        MOV EAX,[EBX+PGM.Pgmopt.Status]
        CMP AL,pgmoptBIN ; Program formats which link at VA rather than RVA.
        JE .61:
        CMP AL,pgmoptPE
        JE .61:
        CMP AL,pgmoptDLL
        JE .61:
        BufferStore [%MsgBuffer],=B'],RVA=',6
        JMP .63:
.61:    BufferStore [%MsgBuffer],=B'],VA=',5
.63:    LEA EDI,[%Number]
        MOV EDX,[ESI+SSS.BottomHigh]
        MOV EAX,[ESI+SSS.BottomLow]
        TEST EDX
        MOV EDX,EDI
        JZ .65:
        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=',7
        LEA EDI,[%Number]
        MOV EAX,[ESI+SSS.TopLow]
        MOV EDX,[ESI+SSS.TopHigh]
        SUB EAX,[ESI+SSS.BottomLow]
        SBB EDX,[ESI+SSS.BottomHigh]
        JNZ .67:
        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 EDI,EDX
        StoD EDI
        SUB EDI,EDX
        BufferStore [%MsgBuffer],EDX,EDI
 .67:   JNSt [ESI+SSS.Status],sssGroup,.79:
        BufferStore [%MsgBuffer],=B',group',6
        PUSH ECX,ESI
          MOV EDI,ESI ; Find segments which belong to group EDI.
          MOV EBX,[%Pgm]
          BufferRetrieve [EBX+PGM.SssPtrBuf]
          SHR ECX,2
 .70:     LODSD
          MOV EBX,EAX
          JNSt [EBX+SSS.Status],sssSegment,.76:
          MOV EAX,[EBX+SSS.GroupPtr]
          CMP EDI,EAX ; Does the segment EBX belong to group EDI?
          JE .73:
          TEST EAX
          JZ .76:
          MOV EAX,[EAX+SSS.GroupPtr] ; Group EAX might be from a linked module.
          CMP EDI,EAX
          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=',7
        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=',7
        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=',9
        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 hired 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 "',27 ; Display header.
     BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt+PGMOPT.OutFilePtr],[EBX+PGM.Pgmopt+PGMOPT.OutFileSize]
     BufferStore [%MsgBuffer],=B'",Global=',9
     Invoke EaBufferReserve::,PgmListGlobals
     MOV [%InpBuffer],EAX
     Invoke EaBufferReserve::,PgmListGlobals
     MOV [%OutBuffer],EAX
      ; Step 1: store pointers to all symbols with nonstandard scope to input buffer in loop .13: .. .16:
     ListGetFirst [EBX+PGM.SymList]
     JZ .40:
.13: 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
     ;   and count and copy them to output buffer in 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 ; EAX is public symbol now.
.23: MOV EBX,[EBX+SYM.Status]
     AND EBX,symImport+symExtern
     SetSt EBX,symGlobal
     SetSt [EAX+SYM.Status],EBX ; Merged public with extern.
.26: BufferStoreDword [%OutBuffer],EAX
.30: ; Update global/public/extern/export/import counter.
     MOV EBX,[EAX+SYM.Status]
     LEA ECX,[%CountExp]
     JSt EBX,symExport,.36:
     LEA ECX,[%CountImp]
     JSt EBX,symImport,.36:
     LEA ECX,[%CountPub]
     JNSt EBX,symPublic,.32:
     JNSt EBX,symExtern,.36:
     JMP .34:
.32: LEA ECX,[%CountExt]
     JSt EBX,symExtern,.36:
.34: LEA ECX,[%CountGlb]
.36: INCD [ECX] ; Increment one of five counters.
     JMP .20:
.40: MOV EAX,[%CountGlb]
     LEA EDI,[%Number]
     MOV EDX,EDI
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStore [%MsgBuffer],=B',Public=',8
     MOV EAX,[%CountPub]
     LEA EDI,[%Number]
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStore [%MsgBuffer],=B',Extern=',8
     MOV EAX,[%CountExt]
     LEA EDI,[%Number]
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStore [%MsgBuffer],=B',eXport=',8
     MOV EAX,[%CountExp]
     LEA EDI,[%Number]
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStore [%MsgBuffer],=B',Import=',8
     MOV EAX,[%CountImp]
     LEA EDI,[%Number]
     StoD EDI
     SUB EDI,EDX
     BufferStore [%MsgBuffer],EDX,EDI
     BufferStore [%MsgBuffer],=W(0x0A0D),2
      ; 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'|        ',9
       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).
       BufferStore [%MsgBuffer],=B'(',1 ; 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.
       BufferStore [%MsgBuffer],=B'{',1 ; List internal name in curly brackets.
       BufferStore [%MsgBuffer],[EBX+SYM.InterNamePtr],[EBX+SYM.InterNameSize]
.50:   BufferStore [%MsgBuffer],=B'}',1
.53:   BufferStore [%MsgBuffer],=B',[',2 ; 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:   BufferStore [%MsgBuffer],=B']:',2
       LEA EDI,[%Number]
       MOV EDX,EDI
       StoH EDI,Size=8
       SUB EDI,EDX
       BufferStore [%MsgBuffer],EDX,EDI   ; List symbol offset.
       BufferStore [%MsgBuffer],=B"h,",2
       MOV EDX,[%Pgm]
       MOV EAX,[EDX+PGM.Pgmopt.Status]
       JNSt EAX,pgmoptExecutable|pgmoptImage,.70:
       CMP AL,pgmoptBIN
       JE .60:
       CMP AL,pgmoptPE
       JE .60:
       CMP AL,pgmoptDLL
       JE .60:
       BufferStoreByte [%MsgBuffer],'R'
.60:   BufferStore [%MsgBuffer],=B'VA=',3
.63:   SUB EAX,EAX
       MOV ECX,[EBX+SYM.Section]
       JECXZ .66:
       MOV ECX,[ECX+SSS.SegmPtr]
       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='",7
       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:
       MOV ECX,[EBX+SYM.DllNameSize]
       JECXZ .78:
       BufferStore [%MsgBuffer],=B',lib="',6
       BufferStore [%MsgBuffer],[EBX+SYM.DllNamePtr],[EBX+SYM.DllNameSize]
       BufferStoreByte [%MsgBuffer],'"'
.78:   ; Display fwd= of forwarded symbols.
       JNSt [EBX+SYM.Status],symForwarded,.79:
       BufferStore [%MsgBuffer],=B',fwd=',5
       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 ending program PGM.
Output
Program.EntryExp is evaluated.
Error
Evaluation error is signalized with Pgm.EntryExp.Status='#' and, if ReportsMsg is true, reported with macro Msg.
See also
ExpReportError
Invokes
ExpEval ExpReportError
Invoked by
PassCreate PfcomCompile PfmzCompile PfomfStoreModule PfpeCompile
PgmEvalEntry Procedure PgmPtr
     MOV EDX,[%PgmPtr]
     LEA EDI,[EDX+PGM.EntryExp]
     MOV ECX,[EDX+PGM.Pgmopt.EntrySize]
     MOV ESI,[EDX+PGM.Pgmopt.EntryPtr]
     StripSpaces ESI,ECX
     JECXZ .90: ; If ENTRY is empty, leave EntryExp empty as well.
     Invoke ExpEval::,EDI,ESI,ECX,[EDX+PGM.CurrentStm]
     Invoke ExpReportError::,EDI
     JC .90:
.50: CMPB [EDI+EXP.Status],'#' ; Did ENTRY evaluated with error?
     JE .90: ; Ignore error if not the final pass.
     MOV ECX,[EDI+EXP.Sym]
     JECXZ .90: ; If the ENTRY= referred some symbol, make it global.
     SetSt [ECX+SYM.Status],symGlobal+symUsed
.90:EndProcedure PgmEvalEntry
↑ PgmLinkSections Pgm
PgmLinkSections is invoked from PseudoENDPROGRAM when assembly pass ended. It is also invoked from PfOutput when linking begins.
This procedure will virtually link all program sections, i.e. update their bottoms, tops, symbols, to their segment.
If Pgm.Status:pgmLinking is set, which happens at link time, it will also concatenate emit and reloc buffer of all sections to their segment and then discard the sections.
Bottom of base segment remains at VA=0.
Input
Pgm Pointer to PGM whose sections should be virtually linked to their segments.
Output
Sections of the program have adjusted their Bottom and Top, symbols and relocations.
Error
Errors are reported with macro Msg.
Invokes
SssLinkSegment
Invoked by
PfOutput PseudoENDPROGRAM
PgmLinkSections Procedure Pgm
     MOV EBX,[%Pgm]
     ListGetFirst [EBX+PGM.SssList]
     JZ .90:
.10: JNSt [EAX+SSS.Status],sssSegment,.80:
     Invoke SssLinkSegment::,EAX,EBX
.80: ListGetNext EAX
     JNZ .10:
.90:EndProcedure PgmLinkSections
↑ PgmCreateImplicitSegments PgmPtr
PgmCreateImplicitSegments will store several implicit segments on program.SssList, depending on program format.
Input
PgmPtr Pointer to just created program.
Output
-
See also
PgmCreateImplicitGroups
Depends on
%PfPgmoptList
Invokes
SssCreate
Invoked by
PseudoPROGRAM
PgmCreateImplicitSegments Procedure PgmPtr
SssStm LocalVar Size=SIZE#STM ; Fake statement to satisfy SssCreate.
      LEA EBX,[%SssStm]
      MOV EDI,[%PgmPtr]
      MOV [EBX+STM.Program],EDI
      MOV EAX,[EDI+PGM.LinePtr]
      MOV [EBX+STM.LinePtr],EAX
      MOV ESI,[EDI+PGM.Pgmopt.SectionAlign]
      CMP ESI,16
      JAE .20:
      MOV ESI,16 ; Default segment alignment is typically 1K in PE|DLL, otherwise at least 16.
 .20: MOV EAX,[EDI+PGM.Pgmopt.Status]
      MOV ECX,pgmoptWidthMask ; Identical with sssWidthMask.
      AND ECX,EAX ; Default segment width is adopted from program width.
      MOV EDX,pgmoptFormatMask
      AND EDX,EAX
      Dispatch EDX,%PfPgmoptList
      Msg '9979' ; Internal error: Missing format handler in PgmCreateImplicitSegments.
; Format handlers input:
; EAX=Pgmopt.Status, EDX=pgmoptFormat, ECX=pgmoptWidth=sssWidth, EDI=^Pgm, EBX=^Stm, ESI=Alignment.
.pgmoptPE: ; 32bit protected-mode COFF-based formats use by default segments [.data], [.bss], [.text], no groups.
.pgmoptDLL:
.pgmoptCOFF:
.pgmoptLIBCOF:
      OR ECX,sssSegment+sssSection+sssImplicit+sssPublic
      ; Create implicit code segment [.text]:
      Invoke SssCreate::,EBX,0,=B".text",5,ECX,sssPurposeCODE,ESI
      ; Create implicit data segment [.data]:
      Invoke SssCreate::,EBX,0,=B".data",5,ECX,sssPurposeDATA,ESI
      ; Create implicit bss segment [.bss]:
      Invoke SssCreate::,EBX,0,=B".bss",4,ECX,sssPurposeBSS,ESI
      JMP .90:
.pgmoptBIN: ; Format BIN uses by default one common segment [BIN], no groups:
      OR ECX,sssSegment+sssSection+sssImplicit+sssPublic
      Invoke SssCreate::,EBX,0,=B"BIN",3,ECX,sssPurposeCODE+sssPurposeDATA+sssPurposeBSS+sssPurposeSTACK,ESI
      JMP .90:
.pgmoptCOM: ; Format COM uses by default one common segment [COM]. It begins with 256bytes virtual section [PSP].
      OR ECX,sssSegment+sssSection+sssImplicit+sssPublic
      Invoke SssCreate::,EBX,0,=B"COM",3,ECX,sssPurposeCODE+sssPurposeDATA+sssPurposeBSS+sssPurposeSTACK,ESI
      JMP .90:
.pgmoptMZ:
.pgmoptOMF: ; 16bit formats OMF and MZ use by default segments [CODE], [DATA], [BSS], [STACK], no groups:
.pgmoptLIBOMF:
      ; Create implicit code segment [CODE]:
      OR ECX,sssSegment+sssSection+sssImplicit+sssPublic
      Invoke SssCreate::,EBX,0,=B"CODE",4,ECX,sssPurposeCODE,ESI
      ; Create implicit data segment [DATA]:
      Invoke SssCreate::,EBX,0,=B"DATA",4,ECX,sssPurposeDATA,ESI
      ; Create implicit bss segment [BSS]:
      Invoke SssCreate::,EBX,0,=B"BSS",3,ECX,sssPurposeBSS,ESI
      ; Create implicit stack segment [STACK]:
      AND ECX,~sssCombineMask
      OR ECX,sssStack
      Invoke SssCreate::,EBX,0,=B"STACK",5,ECX,sssPurposeSTACK,2 ; Implicit stack is WORD aligned.
.pgmoptRSRC: ; Special formats do not create segments.
.90:EndProcedure PgmCreateImplicitSegments
↑ PgmCreateImplicitGroups Program

Programmer may declare explicit group(s) of segments, their names will be respected here. Otherwise this procedure creates implicite group, depending on the chosen memory model.

Procedure CreateImplicitGroups is employed when an executable program is about to link, segments of all linked modules have just been combined but not sorted yet into the final order. If the program format is not executable, or if MODEL=FLAT, this procedure does nothing.

When a monocode program is created (MODEL=TINY | SMALL | COMPACT), it usually has just one public code segment. If more than one code segments with different names are combined (from linked modules, for instance), they will be grouped here into an implicit code group which should be used as their addressing frame. The name of the implicite group is the same as its first segment. Code transfers withing implicit code group are by default NEAR and their relocations can be resolved at link time.
In multicode program models (MODEL=MEDIUM | LARGE | HUGE) each segment, which wasn't assigned to an explicit group yet, will be accompanied with its own implicit group of the same name.

Similary in monodata programs (MODEL=TINY | SMALL | MEDIUM) data and BSS segments are grouped into an implicit data group.
In multidata models (MODEL=COMPACT | LARGE | HUGE ), each nonstack segment without PURPOSE=CODE and without explicit group will be assigned to its own implicit group with the same name.

Pure stack segments (PURPOSE=STACK) in nonflat model are grouped to an implicit stack group.

If MODEL=TINY, one common implicit group with the same name as its first segment will be created here for all segments of any purpose.

Input
Program Pointer to PGM whose segments will be regrouped.
Output
-
Error
Errors are reported with macro Msg.
See also
PgmCreateImplicitSegments
Invoked by
PfbinCompile PfcomCompile PfmzCompile
PgmCreateImplicitGroups Procedure Program
     MOV EBX,[%Program]
     CMPB [EBX+PGM.Pgmopt.Status],pgmoptBIN
     JE .05:
     JNSt [EBX+PGM.Pgmopt.Status],pgmoptExecutable,.90:
.05: JSt [EBX+PGM.Pgmopt.Status],pgmoptFLAT,.90:
     ; First segments pass will find explicit code, data, stack groups in a loop .10: .. .20:.
     SUB ECX,ECX ; Pointer to the 1st code  group (CGROUP).
     SUB EDI,EDI ; Pointer to the 1st data  group (DGROUP).
     SUB ESI,ESI ; Pointer to the 1st stack group (SGROUP).
     ListGetFirst [EBX+PGM.SssList]
     JZ .90:
.10: JNSt [EAX+SSS.Status],sssSegment,.20:
     CMP [EAX+SSS.Purpose],sssPurposeSTACK
     JNE .13:
     TEST ESI  ; Was SGROUP already found?
     JNZ .13:
     MOV ESI,[EAX+SSS.GroupPtr]
.13: JNSt [EAX+SSS.Purpose],sssPurposeCODE,.15:
     TEST ECX ; Was CGROUP already found?
     JNZ .15:
     MOV ECX,[EAX+SSS.GroupPtr] ; Use the first code segment as CGROUP.
.15: JNSt [EAX+SSS.Purpose],sssPurposeDATA|sssPurposeBSS,.20:
     TEST EDI ; Was DGROUP already found?
     JNZ .20:
     MOV EDI,[EAX+SSS.GroupPtr]
.20: ListGetNext EAX
     JNZ .10:
     JNSt [EBX+PGM.Pgmopt.Status],pgmoptTINY,.25:
     ; TINY model will have one common group used as addressing frame.
     TEST ECX
     JNZ .23: ; Prefer CGROUP, if exists.
     MOV ECX,EDI
     TEST ECX
     JNZ .23:
     MOV ECX,ESI
     TEST ECX
     JNZ .23:
     ; No explicit group was declared in TINY model,
     ;  so clone an implicite group from the 1st segment.
     ListGetFirst [EBX+PGM.SssList]
     CALL .MakeGroup:
     MOV ECX,EDX
     SetSt [ECX+SSS.Purpose],sssPurposeCODE+sssPurposeDATA+sssPurposeBSS+sssPurposeSTACK
.23: MOV EDI,ECX
     MOV ESI,ECX
.25: ; ECX,EDI,ESI are now explicit code, data, stack groups, or 0 if no such group was declared in sources.
     ; Second segment pass in the loop .30: .. .80:.
     ; Each nongrouped-yet segment will be assigned to a group,
     ; either explicit when ECX/EDI/ESI is nonzero, or implicite (cloned from the segment).
     ListGetFirst [EBX+PGM.SssList]
.30: JNSt [EAX+SSS.Status],sssSegment,.80: ; Skip if EAX is a group.
     CMP [EAX+SSS.Purpose],sssPurposeSTACK
     JNE .40:
     ; Stack segment regroup to implicit group ESI.
     TEST ESI
     JNZ .35:   ; Skip if implicit stack group exists.
     CALL .MakeGroup:
     MOV ESI,EDX ; ESI is now the cloned group.
.35: MOV [EAX+SSS.GroupPtr],ESI ; Assign the segment EAX to group ESI.
     JMP .80:

.MakeGroup:PROC ; Create a new group as a clone of segment EAX.
     ; Input: EAX=^SSS with ungrouped segment, EBX=^PGM.
     ; Output: EDX=^SSS with the cloned group.
       MOV EDX,EAX 
       ListStore [EBX+PGM.SssList],EDX
       XCHG EAX,EDX 
       SetSt [EDX+SSS.Status],sssGroup+sssImplicit
       RstSt [EDX+SSS.Status],sssSegment
       MOV [EDX+SSS.GroupPtr],EDX ; Group always refers to itself.
       MOVD [EDX+SSS.EmitBuffer],0 ; No emit and reloc buffer in groups.
       MOVD [EDX+SSS.RelocBuffer],0
       RET
     ENDP .MakeGroup:

.40: JNSt [EAX+SSS.Purpose],sssPurposeCODE,.60:
     ; Code segment (implicit group is ECX).
     JSt [EBX+PGM.Pgmopt.Status],pgmoptTINY | pgmoptSMALL | pgmoptCOMPACT, .50:
     ; EAX is code segment in multicode model. Regroup to CGROUP only if it wasn't in a group.
     CMPD [EAX+SSS.GroupPtr],0
     JNZ .80:
     CALL .MakeGroup:
     MOV [EAX+SSS.GroupPtr],EDX
     JMP .80:
.50: ; EAX is code segment in monocode model. Regroup segment EAX to group ECX.
     TEST ECX
     JNZ .55:
     CALL .MakeGroup:
     MOV ECX,EDX
.55: MOV [EAX+SSS.GroupPtr],ECX
     JMP .80:

.60: JNSt [EAX+SSS.Purpose],sssPurposeDATA|sssPurposeBSS,.80:
     ; Data segment (implicit group is EDI).
     JSt [EBX+PGM.Pgmopt.Status],pgmoptTINY | pgmoptSMALL | pgmoptMEDIUM, .70:
     ; EAX is data segment in multidata model. Make self-group if it wasn't in a group.
     CMPD [EAX+SSS.GroupPtr],0
     JNZ .80:
     CALL .MakeGroup:
     MOV [EAX+SSS.GroupPtr],EDX
     JMP .80:
.70: ; EAX is data segment in monodata model. Regroup segment EAX to group EDI.
     TEST EDI
     JNZ .75:
     CALL .MakeGroup:
     MOV EDI,EDX
.75: MOV [EAX+SSS.GroupPtr],EDI
.80: ListGetNext EAX ; The next segment.
     JNZ .30:
.90:EndProcedure PgmCreateImplicitGroups
↑ PgmOrderSegments Pgm
This procedure decides about segment ordering in output program file, according to rules specified in manual in chapter Ordering of segments.
Input
Pgm points to PGM - program whose segments are being sorted. Virtual address of each segment is 0.
Output
Pgm.SssPtrBuf is filled with DWORD pointers to SSS objects (groups and segments) in their final order.
Number of stored groups and segments, decremented by the number of trailing uninitialized objects, is written to Pgm.NrOfInitSegments. If there are more pointers in Pgm.SssPtrBuf than Pgm.NrOfInitSegments, they all are uninitialized and their raw data may be omitted from image.
Manual
Ordering of segments
Invoked by
PfbinCompile PfcoffCompile PfcomCompile PfmzCompile PfomfStoreModule PfpeCompile
Invokes
PgmOrderSegmDo
Tested by
t7901 t7904
PgmOrderSegments Procedure Pgm
 ; Define three internal subprocedures:

.UpdateGroup:PROC1 ; Update .Status:sssNotBSS flag of the group.
    ; Input: EAX=^group, EDX=^Pgm.  Destroys EDI.
    PUSH ECX
     MOV EDI,EAX ; EDI is now the updated group in base segment.
     ; Its flag sssNotBSS will be updated from all its segments.
     ListGetFirst [EDX+PGM.SssList]
 .U1:JNSt [EAX+SSS.Status],sssSegment,.U8:
     ; EAX is now a segment of program EDX.
     MOV ECX,[EAX+SSS.GroupPtr] ; ECX is now the group of segment EAX.
     CMP EDI,ECX
     JE .U6: ; If it is our segment.
     JECXZ .U8: ; ECX might be a group from linked segment.
     MOV ECX,[ECX+SSS.GroupPtr]
     CMP EDI,ECX
     JNE .U8:
 .U6:; EAX is our segment. Update our group EDI from its flag.
     JNSt [EAX+SSS.Status],sssNotBSS,.U8:
     SetSt [EDI+SSS.Status],sssNotBSS ; At least one segment of the group EDI has initialized data.
     JMP .U9: ; No need to search further.
 .U8:ListGetNext EAX ; The next segment.
     JNZ .U1:
 .U9:MOV EAX,EDI ; Restore group EAX.
    POP ECX
    RET
    ENDP1 .UpdateGroup:

.StoreInitGroup:PROC1 ; Store ptr to initialized group and all its segments.
    ; Input: EAX=^group, EDX=^Pgm. EBX=output buffer. Destroys ECX,ESI,EDI.
    MOV EDI,EAX ; Group with flag sssNotBSS set.
    BufferStoreDword EBX,EDI ; Store initialized group EDI.
    BufferRetrieve EBX
    MOV ESI,ECX ; Save index to the 1st initialized grouped segment (for PgmOrderSegmDo).
    ListGetFirst [EDX+PGM.SssList] ; Now store all segments of group EDI.
.I2:JNSt [EAX+SSS.Status],sssSegment,.I8:
    MOV ECX,[EAX+SSS.GroupPtr] ; ECX is now the group of segment EAX.
    CMP EDI,ECX
    JE .I6: ; If segment EAX belongs to the group EDI.
    JECXZ .I8:
    MOV ECX,[ECX+SSS.GroupPtr] ; ECX might be a group from linked segment.
    CMP EDI,ECX
    JNE .I8:
.I6:BufferStoreDword EBX,EAX ; Store initialized segment EAX from group EDI.
.I8:ListGetNext EAX
    JNZ .I2: ; The next segment.
    Invoke PgmOrderSegmDo,EBX,ESI ; Reorder segments with $ in their name.
    MOV EAX,EDI ; Restore group EAX.
    RET
    ENDP1 .StoreInitGroup:

.StoreBssGroup:PROC1 ; ; Store ptr to uninitialized group and all its segments.
    ; Input: EAX=^group, EDX=^Pgm. EBX=output buffer. Destroys ECX,ESI,EDI.
    MOV EDI,EAX ; Group with flag sssNotBSS reset.
    BufferStoreDword EBX,EDI ; Store uninitialized group EDI.
    BufferRetrieve EBX
    MOV ESI,ECX ; Save index to the 1st uninitialized grouped segment (for PgmOrderSegmDo).
    ListGetFirst [EDX+PGM.SssList] ; Store all segments of group EDI.
.B2:JNSt [EAX+SSS.Status],sssSegment,.B8:
    MOV ECX,[EAX+SSS.GroupPtr] ; ECX is now the group of segment EAX.
    CMP EDI,ECX
    JE .B6: ; If segment EAX belongs to the group EDI.
    JECXZ .B8:
    MOV ECX,[ECX+SSS.GroupPtr] ; ECX might be a group from linked segment.
    CMP EDI,ECX
    JNE .B8:
.B6:BufferStoreDword EBX,EAX ; Store segment EAX from group EDI.
.B8:ListGetNext EAX
    JNZ .B2: ; The next segment.
    Invoke PgmOrderSegmDo,EBX,ESI  ; Reorder segments with $ in their name.
    MOV EAX,EDI ; Restore group EAX.
    RET
    ENDP1 .StoreBssGroup:

    ; Start the main procedure PgmOrderSegments:
    MOV EDX,[%Pgm]
    MOV EBX,[EDX+PGM.SssPtrBuf] ; The output buffer for ordered segments pointers.
    TEST EBX
    JNZ .10:
    BufferCreate [EDX+PGM.Pool],Size=64
    MOV [EDX+PGM.SssPtrBuf],EAX
    MOV EBX,EAX
.10:BufferClear EBX
    ; Pass 1 - for each group update its SSS.Status:sssNotBSS flag.
    ListGetFirst [EDX+PGM.SssList]
    JZ .90:
.11:JNSt [EAX+SSS.Status],sssGroup,.19:
    CALL .UpdateGroup:
.19:ListGetNext EAX ; The next group.
    JNZ .11:

    ; Pass 2: store initialized groups and all their segments.
    ListGetFirst [EDX+PGM.SssList]
.21:JNSt [EAX+SSS.Status],sssGroup,.29:
    JNSt [EAX+SSS.Status],sssNotBSS,.29:
    CALL .StoreInitGroup:
.29:ListGetNext EAX ; The next group.
    JNZ .21:

    ; Pass 3: store initialized nongrouped segments.
    XOR ESI,ESI
    DEC ESI ; Initialize the PgmOrderSegmDo index as unused (-1).
    ListGetFirst [EDX+PGM.SssList]
.31:JNSt [EAX+SSS.Status],sssSegment,.39:
    CMPD [EAX+SSS.GroupPtr],0 ; Nongrouped segment?
    JNZ .39:
    JNSt [EAX+SSS.Status],sssNotBSS,.39:
    BufferStoreDword EBX,EAX ; Store initialized ungrouped segment EAX.
    TEST ESI
    JNS .39: ; Skip if index is already marked in ESI.
    BufferRetrieve EBX
    LEA ESI,[ECX-4]  ; Save index to the 1st initialized ungrouped segment (for PgmOrderSegmDo).
.39:ListGetNext EAX
    JNZ .31: ; The next segment.
    Invoke PgmOrderSegmDo,EBX,ESI ; Reorder segments with $ in their name.

    ; Pass 4: store uninitialized groups and all their segments.
    ListGetFirst [EDX+PGM.SssList]
.41:JNSt [EAX+SSS.Status],sssGroup,.49:
    JSt [EAX+SSS.Status],sssNotBSS,.49:
    CALL .StoreBssGroup:
.49:ListGetNext EAX ; The next group.
    JNZ .41:
    ; Pass 5: store uninitialized nongrouped segments.
    XOR ESI,ESI
    DEC ESI ; Initialize the PgmOrderSegmDo index as unused.
    ListGetFirst [EDX+PGM.SssList]
.51:JNSt [EAX+SSS.Status],sssSegment,.59:
    CMPD [EAX+SSS.GroupPtr],0
    JNZ .59:
    JSt  [EAX+SSS.Status],sssNotBSS,.59:
    BufferStoreDword EBX,EAX ; Store uninitialized ungrouped segment EAX.
    TEST ESI
    JNS .59: ; Skip if index is already marked in ESI.
    BufferRetrieve EBX
    LEA ESI,[ECX-4]  ; Save index to the 1st object for PgmOrderSegmDo.
.59:ListGetNext EAX
    JNZ .51: ; The next segment.
    Invoke PgmOrderSegmDo,EBX,ESI
    ; Step 6: find number of initialized objects in .SssPtrBuf EBX.
    BufferRetrieve EBX
    MOV EBX,ECX
    SHR EBX,2 ; Number of all stored SSS objects.
    ; Walk through the objects backward.
.62:CMP ECX,4
    JB .69: ; If buffer bottom reached.
    MOV EAX,[ESI+ECX-4] ; The last stored pointer.
    JSt [EAX+SSS.Status],sssNotBSS,.69: ; If the last initialized segment found.
    DEC EBX ; Segment EAX is uninitialized, omit it from output count.
    SUB ECX,4
    JMP .62:
.69:MOV [EDX+PGM.NrOfInitSegments],EBX
.90:EndProcedure PgmOrderSegments
↑ PgmOrderSegmDo SegmPtrBuffer, Index1st
This procedure will reorder pointers to SSS segments in SegmPtrBuffer, starting with the pointer specified by Index1st up to the end of buffer.
Segment with $ in their name with identical substring on the left of $ will be sorted alphabetically.
Input
SegmPtrBuffer points to BUFFER with pointers to segments.
Index1st=0,4,8,12.. is pointer relative to buffer contents to the first concerned segment. This one and all following pointers up to the end of buffer will be investigated and sorted.
Output
Investigated pointers might be reorganized to follow ordering rules.
Invoked by
PgmOrderSegments
Invokes
EaBufferRelease EaBufferReserve EaBufferSort
Tested by
t7901 t7904
PgmOrderSegmDo Procedure SegmPtrBuffer, Index1st
OutBuf      LocalVar
SortBuf     LocalVar
Prefix$Ptr  LocalVar ; Pointer to segment name.
Prefix$Size LocalVar ; Number of characters up to $.
    Invoke EaBufferReserve::,PgmOrderSegmDo
    MOV [%OutBuf],EAX
    Invoke EaBufferReserve::,PgmOrderSegmDo
    MOV [%SortBuf],EAX
    BufferRetrieve [%SegmPtrBuffer]
    LEA EDX,[ESI+ECX] ; End of pointers.
    MOV ECX,[%Index1st]
    TEST ECX
    JS .90:
    BufferStore [%OutBuf],ESI,ECX ; Copy the fixed part of input buffer.
    ADD ESI,ECX ; ESI..EDX is an array of pointers to the investigated segments.
    JMP .20:
.10:BufferStoreDword [%OutBuf],EBX
.20:CMP ESI,EDX
    JNB .80:
    LODSD
    MOV EBX,EAX
    TEST EBX
    JZ .20: ; Skip if this segment was already stored to %OutBuf.
    JNSt [EBX+SSS.Status],sssSegment,.10:
    MOV EDI,[EBX+SSS.NamePtr]
    MOV ECX,[EBX+SSS.NameSize]
    MOV [%Prefix$Ptr],EDI
    MOV AL,'$'
    REPNE SCASB
    JNE .10:
    SUB EDI,[EBX+SSS.NamePtr]
    MOV [%Prefix$Size],EDI
    BufferStoreDword [%SortBuf],EBX ; EBX is segment with $ in name.
    MOVD [ESI-4],0 ; Mark the input segment as investigated.
    ; Now find and store all remaining segments matching [%Prefix$size].
    PUSH ESI
.30:  CMP ESI,EDX
      JNB .40:
      LODSD
      MOV EBX,EAX
      TEST EBX
      JZ .30:
      PUSH ESI
        MOV ECX,[%Prefix$Size]
        MOV ESI,[%Prefix$Ptr]
        MOV EDI,[EBX+SSS.NamePtr]
        REPE CMPSB
      POP ESI
      JNE .30:
      ; Another matching segment found.
      BufferStoreDword [%SortBuf],EBX
      MOVD [ESI-4],0 ; Mark the input segment as investigated.
      JMP .30:
.40:  Invoke EaBufferSort::,[%SortBuf]
      BufferRetrieve [%SortBuf]
      BufferStore [%OutBuf],ESI,ECX
      BufferClear [%SortBuf] ; Prepare %SortBuf for the next group.
.50:POP ESI
    JMP .20:
.80: ; Copy re-sorted pointers from temporary %OutBuf the input/output %SegmPtrBuffer.
    BufferClear [%SegmPtrBuffer]
    BufferRetrieve [%OutBuf]
    BufferStore [%SegmPtrBuffer],ESI,ECX
.90:Invoke EaBufferRelease::,[%OutBuf]
    Invoke EaBufferRelease::,[%SortBuf]
    EndProcedure PgmOrderSegmDo
↑ PgmCombine BasePgm

PgmCombine merges linkable programs which were loaded from disk file(s) by PfLoad, stored on Pgm.ModulePgmList and marked pgmSelected by PgmSelectModules.
Global symbols, all groups and segments including their emitted contents and relocations are copied to the base program.

Public segments with matching names are concatenated into one segment and offset of symbols and relocations from appended public segments is adjusted to correspond with updated segment bottom.
Common and stack-combined segments with the same name are merged into one segment, too. Private segments are simply added to the BasePgm.SssList.

When PgmCombine ends its job, extern symbols coexist in BasePgm with public symbols of the same name, as well as with their corresponding extern pseudosegments. The process of matching EXTERN and PUBLIC will take place later in PgmLinkImage or in 3rd party linker.

Relocations in joined public segment are fixed-up relative to their segment bottom but not finally resolved, because all segments of BasePgm still origin at RVA=0 and their order is not set yet. Linking of segments to an output image will take place later in PgmLinkImage invoked by PfFormatCompile.

Segments of linked/imported programs, which were loaded on Pgm.ModulePgmList , are emptied and they may be abandoned when PgmCombine terminates, because information from all loaded programs is completely combined into the BasePgm.

This procedure is not invoked when BasePgm is a library in LIBOMF or LIBCOF format.

Input
BasePgm Pointer to the main (base) program which is already assembled and which it is linked to.
Output
All referenced loaded programs (modules) from BasePgm.ModulePgmList are combined to the BasePgm.
Error
Errors are reported with macro Msg.
Invoked by
PfOutput
Invokes
RelocFixup SssCombine SssCreateExtern SssResizeGroup SymFindByName SymFixup
Tested by
t7940 t7943
PgmCombine Procedure BasePgm
    MOV EBX,[%BasePgm]
    ListGetFirst [EBX+PGM.ModulePgmList]
    JZ .24:
.10:MOV EDI,EAX ; EDI is now one of the modules which were loaded by LINK statement.
    JNSt [EDI+PGM.Status],pgmSelected,.20: ; Skip the unused library module (smart linking).
.12:; Combine segments, groups, externs of loaded module EDI to the base program EBX in loop .14: .. .16:.
    ListGetFirst [EDI+PGM.SssList] ; EAX is now one of the SSS objects of linked module.
    JZ .20:
.14:Invoke SssCombine::,EAX,EBX ; Combine linked segment/group/extern EAX to the main program EBX.
.16:ListGetNext EAX
    JNZ .14: ; The next SSS object of linked module EDI.
.20:ListGetNext EDI ; The next linked/imported program.
    JNZ .10:
.24:; Update each group's .Bottom and .Top to match the lowest and highest VA of all its segments.
    ListGetFirst [EBX+PGM.SssList] ; Walk through groups in BasePgm.
    JZ .34:
.26:JNSt [EAX+SSS.Status],sssGroup,.30:
    Invoke SssResizeGroup::,EAX,EBX
.30:ListGetNext EAX
    JNZ .26: ; The next group.
.34:; Combine and fixup program entry plus global symbols from all referenced modules in loop .36: .. .64:.
    ListGetFirst [EBX+PGM.ModulePgmList]
    JZ .82:
.36:MOV EDI,EAX ; Linked module.
    JSt [EBX+PGM.Pgmopt.Status],pgmoptLinkable,.37: ; Combine nonreferenced module when a linkable is compiled.
    JNSt [EDI+PGM.Status],pgmSelected,.64:          ; Skip unused library module.
.37:LEA ECX,[EDI+PGM.EntryExp]
    CMPB [ECX+EXP.Status+0],'A'
    JNE .40:                                        ; Skip if evaluated entry is scalar or none.
    ; Update evaluated address entry symbol.
    MOV ESI,[ECX+EXP.Seg]
    TEST ESI
    JNZ .38:
    Msg '7711',EDI               ; Invalid program entry point "!1S".
    JMP .40:
.38:JNSt [ESI+SSS.Status],sssSegment,.40: ; Skip if the entry is an external symbol.
    MOV EAX,[ESI+SSS.BottomLow]  ; Bottom of linked segment might have been elevated
    MOV EDX,[ESI+SSS.BottomHigh] ;    from 0 to delta EDX:EAX in first combine pass.
    MOV ESI,[ESI+SSS.SegmPtr]
    SUB EAX,[ESI+SSS.BottomLow]
    SBB EDX,[ESI+SSS.BottomHigh]
    ADD [ECX+EXP.Low],EAX        ; Fixup the evaluated entry symbol by delta.
    ADC [ECX+EXP.High],EDX
    MOV [ECX+EXP.Seg],ESI
    ; Updated evaluated entry ECX from linked program EDI will be copied to base program EBX.
    LEA ESI,[EBX+PGM.EntryExp]
    CMPD [ESI+EXP.Status],0      ; Evaluated entry in base program should be empty.
    Msg cc=NZ,'8544',EDI,EBX     ; The entry specified in program "!1S" collides with entry in "!2S".
    JNZ .40:
    CopyTo ESI,ECX,Size=SIZE#EXP ; Copy evaluated entry ECX to the base program.
.40:; Copy and update global symbols from linked module EDI in the loop .46: .. .60:.
    ListGetFirst [EDI+PGM.SymList]
    JZ .64:                      ; If no symbol exists in module EDI.
.46:MOV ESI,EAX                  ; Symbol ESI from linked module EDI is global (otherwise it wouldn't get loaded).
    MOV ECX,[ESI+SYM.Section]    ; Symbol ESI might have been defined in a segment from linked Pgm.
    JECXZ .50:                   ; If the symbol is scalar.
    MOV EAX,[ECX+SSS.BottomLow]  ; Bottom is nonzero if symbol ESI was defined in the linked segment ECX,
    MOV EDX,[ECX+SSS.BottomHigh] ;    it might have been elevated from 0 to delta EDX:EAX.
    MOV ECX,[ECX+SSS.SegmPtr] ; ECX now refers to the new homonymous segment in base program.
    ; If ECX is an extern pseudosegment, it hasn't been elevated, so the fixup delta is void (EDX:EAX=0).
    ADD [ESI+SYM.OffsetLow],EAX  ; Fixup the symbol by delta EDX:EAX.
    ADC [ESI+SYM.OffsetHigh],EDX
    MOV [ESI+SYM.Section],ECX
.50:JNSt [ESI+SYM.Status],symPublic|symExport,.54:
    ; Combine linked public|exported symbol ESI into base program EBX.
    Invoke SymFindByName::,symPublic|symExport,[ESI+SYM.NamePtr],[ESI+SYM.NameSize],EBX
    JC .53:
    MOV ECX,[EAX+SYM.Status]
    JSt ECX,symDefined,.E8540:
    AND ECX,symImport+symExport
    CopyTo EAX,ESI,Size=SIZE# SYM
    SetSt [EAX+SYM.Status],ECX
    JMP .60:
.E8540:Msg '8540',ESI,[EAX+SYM.LinePtr] ; Public symbol "!1S" was already defined at !2@.
.53:ListStore [EBX+PGM.SymList],ESI     ; Copy global symbol ESI to the base program EBX.
    JMP .60:
.54:JNSt [ESI+SYM.Status],symExtern|symImport,.60:
    ; Combine linked external|imported symbol ESI into base program EBX.
    Invoke SymFindByName::,symExtern|symImport,[ESI+SYM.NamePtr],[ESI+SYM.NameSize],EBX
    JNC .56:                        ; Skip when symbol with this name already is in base program.
    ListStore [EBX+PGM.SymList],ESI ; Copy global symbol ESI to the base program EBX.
.56:; EAX is now a homonymous external|imported symbol in base program EBX.
    ; Update some of its properties from symbol ESI.
    MOV ECX,symPropMask+symDefined+symReferenced+symQueried+symUsed+symDefInPass+symFixed+symImport
    AND ECX,[ESI+SYM.Status]
    OR [EAX+SYM.Status],ECX
    ; Let's update base symbol EAX if linked symbol ESI is imported and EAX is not.
    ; This happens when a symbol is global or double-colon-terminated in base program
    ;   and explicitly declared as IMPORT in linked module.
    MOV ECX,[ESI+SYM.DllNameSize]
    JECXZ .58:
    MOV [EAX+SYM.DllNameSize],ECX
    MOV ECX,[ESI+SYM.DllNamePtr]
    MOV [EAX+SYM.DllNamePtr],ECX
.58:Invoke SssCreateExtern::,EAX,EBX ; Make sure that is has its pseudosegment.
.60:ListGetNext ESI                  ; The next symbol.
    JNZ .46:
.64:ListGetNext EDI                  ; The next linked program.
    JNZ .36:
    ; Fixup all symbols combined now in base program EBX.
    ListGetFirst [EBX+PGM.SymList]
    JZ .70:
.66:Invoke SymFixup::,EAX            ; Elevate symbol offset if it was defined in concatenated segment.
    ListGetNext EAX
    JNZ .66:
.70:; Fixup all relocations combined now in base program EBX.
    ListGetFirst [EBX+PGM.SssList]
    JZ .90:
.74:BufferRetrieve [EAX+SSS.RelocBuffer]
    JECXZ .80:
.77:Invoke RelocFixup::,ESI
    ADD ESI,SIZE# RELOC
    SUB ECX,SIZE# RELOC
    JA .77:
.80:ListGetNext EAX
    JNZ .74:                         ; Relocations in the next segment.
.82:; Add default library for imported symbols without explicit LIB= specification.
    ListGetFirst [EBX+PGM.SymList]
    JZ .90:
.84:JNSt [EAX+SYM.Status],symImport,.88:
    MOV ECX,[EAX+SYM.DllNameSize]
    TEST ECX
    JNZ .88:
    MOV [EAX+SYM.DllNamePtr],=B'%EaDefaultDllName'
    MOV [EAX+SYM.DllNameSize],%EaDefaultDllNameSize
.88:ListGetNext EAX
    JNZ .84:
.90:MOVD [EBX+PGM.ModulePgmList],0   ; Invalidate loaded modules, though they still occupy Pgm.Pool.
   EndProcedure PgmCombine
↑ PgmLinkImage Pgm, StubSize

PgmLinkImage is invoked when assembly of the executable | image (BIN, COM, MZ, PE, DLL) program format ends, all sections have been joined within their segment by SssLinkSection , all segments from all linked programs have been combined by PgmCombine, groups and segments were sorted by PgmOrderSegments and their pointers stored to Pgm.SssPtrBuf in the final order.

PgmLinkImage links program segments as PRIVATE one after another into virtual image.
Image starts at virtual address (VA) specified by PROGRAM IMAGEBASE=.
Alignment of segments withing the image is calculated as the greatest value from segment.Align, SectionAlign, FileAlign. Each segment's .Bottom and .Top is adjusted to their new aligned VA.
The actual raw contents is not concatenated yet by this procedure, see PfFormatCompile.
Bottoms of all segments were zero (related to RVA=0) when PgmLinkImage was invoked but this will be changed here.

Input
Pgm is pointer to program whose segments are being linked into virtual image.
StubSize is size of PE/DLL stub program. 0 in formats BIM, COM, MZ.
Output
Segments and groups of the program have adjusted their Bottom and Top.
Error
Errors are reported with macro Msg.
Invokes
ExpAlign RelocResolveImage SssResizeGroup SymResolveImage
Invoked by
PfbinCompile PfcomCompile PfmzCompile PfpeCompile
Tested by
t7921 t7936 t7938 t7940
PgmLinkImage Procedure Pgm, StubSize
OrgLow     LocalVar ; Image virtual address. It starts at Pgm.Pgmopt.ImageBase.
OrgHigh    LocalVar ; High dword of image VA.
Alignment  LocalVar ; Strongest alignment from PGMOPT.FileAlign and PGMOPT.SectionAlign.
Base       LocalVar ; PGMOPT.ImageBaseLow in format BIN, otherwise 0.
    XOR ECX,ECX
    MOV EBX,[%Pgm]
    MOV [%Base],ECX
    ; Initialize Org with ImageBase.
    MOV EAX,[EBX+PGM.Pgmopt.ImageBaseLow]
    MOV EDX,[EBX+PGM.Pgmopt.ImageBaseHigh]
    MOV ECX,[EBX+PGM.Pgmopt.Status]
    CMP CL,pgmoptBIN
    JNE .10:
    MOV [%Base],EAX
.10:; Formats PE and DLL will reserve at least one section alignment at the beginning of image
    ;  for the copy of stub. VA of other executable images starts directly at ImageBase.
    ADD EAX,[%StubSize]
    ADC EDX,0
    MOV [%OrgLow],EAX
    MOV [%OrgHigh],EDX
    ; Initialize image alignment.
    MOV EAX,[EBX+PGM.Pgmopt+PGMOPT.FileAlign]
    MOV EDX,[EBX+PGM.Pgmopt+PGMOPT.SectionAlign]
    CMP EAX,EDX
    JAE .15:
    XCHG EAX,EDX
.15:MOV [%Alignment],EAX ; Maximum from FileAlign and SectionAlign.
    ; Compute new VA of bottom and top for each segment.
    ; The segment order has already been specified in Pgm.SssPtrBuf.
    ; All segments start at .Bottom=0.
    BufferRetrieve [EBX+PGM.SssPtrBuf] ; ESI,ECX is now a sorted array of pointers to SSS.
    SHR ECX,2 ; Number of linked segments.
    JZ .90: ; If no segments in image.
.20:PUSH ECX
      LODSD ; Load pointer to the linked segment.
      JNSt [EAX+SSS.Status],sssSegment,.40: ; Skip groups in this pass.
      MOV ECX,[EAX+SSS.Alignment] ; Segment's own alignment.
      CMP ECX,[%Alignment]
      JA .30:
      MOV ECX,[%Alignment]
.30: ; ECX is now the strongest alignment from segment/file/section align.
      MOV EDI,[%OrgLow]
      MOV EDX,[%OrgHigh]
      Invoke ExpAlign::,EDI,ECX,0 ; Return unaligned stuff size in ECX.
      ADD EDI,ECX
      ADC EDX,0 ; EDX:EDI is now the new aligned segment VA.
      SUB EDI,[EAX+SSS.BottomLow]
      SBB EDX,[EAX+SSS.BottomHigh] ; EDI:EDX is now delta of relocation.
      ADD [EAX+SSS.BottomLow],EDI
      ADC [EAX+SSS.BottomHigh],EDX
      ADD [EAX+SSS.TopLow],EDI
      ADC [EAX+SSS.TopHigh],EDX
      MOV EDI,[EAX+SSS.TopLow]
      MOV EDX,[EAX+SSS.TopHigh]
      MOV [%OrgLow],EDI
      MOV [%OrgHigh],EDX
.40:POP ECX
    LOOP .20: ; The next segment.
    ; For each group compute its new VA of bottom and top (it will be shown in ListMap).
    BufferRetrieve [EBX+PGM.SssPtrBuf] ; ESI,ECX is now a sorted array of pointers to SSS.
    SHR ECX,2
.50:LODSD
    JNSt [EAX+SSS.Status],sssGroup,.60:
    Invoke SssResizeGroup::,EAX,EBX
.60:LOOP .50:
    ; Match extern/imported symbols to the public ones.
    Invoke SymResolveImage::,EBX
    ; Resolve relocations in each segment.
    BufferRetrieve [EBX+PGM.SssPtrBuf] ; ESI,ECX is now an sorted array of pointers to SSS.
    SAR ECX,2
.70:LODSD
    JNSt [EAX+SSS.Status],sssSegment,.80:
    Invoke RelocResolveImage::,EAX,EBX
 .80:LOOP .70:
.90:EndProcedure PgmLinkImage
↑ PgmConcatenateImage Pgm

PgmConcatenateImage will allocate a buffer on Pgm.Pool and fill the buffer with concatenated raw emitted contents of initialized program segments.
It is invoked when executable output file BIN, COM, MZ (but not PE, DLL ) is compiled.

Segment order is specified by Pgm.SssPtrBuf.
Only initialized segments are streamed. Their count was stored to Pgm.NrOfInitSegments by PgmLinkImage.
Differences between virtual size (Segment.Top - Segment.Bottom ) and emitted size, if any, are stuffed with 0 or with NOP (if both segments have PURPOSE=CODE.

Total allocated size of the buffer is the sum of virtual sizes of all initialized segments.
Netto buffered size is less when the emitted size of the last segment is lower than its virtual size.

Input
Pgm Pointer to a completely assembled, combined, linked PGM.
Output
EAX is pointer to BUFFER with raw image contents.
Error
-
Invoked by
PfbinCompile PfcomCompile PfmzCompile
Invokes
EaBufferRelease EaBufferReserve
PgmConcatenateImage Procedure Pgm
SegmPtrBuf LocalVar ; Temporary buffer with max. Pgm.NrOfInitSegments number of pointers (groups are omitted).
SegmLeft   LocalVar ; Number of not-yet-streamed segments.
EmitSize   LocalVar ; Size of segment.EmitBuffer.
VirtSize   LocalVar ; segment.Top - segment.Bottom.
TotalSize  LocalVar ; Sum of virtual sizes of all initialized segments.
    MOV EBX,[%Pgm]
    Invoke EaBufferReserve::,PgmConcatenateImage
    MOV [%SegmPtrBuf],EAX
    MOV EDX,EAX
    ; Copy pointers to initialized segments from Pgm.SssPtrBuf to %SegmPtrBuf EDX.
    BufferRetrieve [EBX+PGM.SssPtrBuf] ; Pointers to groups and segments.
    SHR ECX,2
    JZ .25: ; If no segment to stream.
    MOV EAX,[EBX+PGM.NrOfInitSegments]
    CMP ECX,EAX
    JBE .10:
    XCHG EAX,ECX
    JECXZ .25: ; If no initialized segment to stream.
.10:LODSD
    JNSt [EAX+SSS.Status],sssSegment,.20: ; Skip groups.
    BufferStoreDword EDX,EAX
.20:LOOP .10:
.25:BufferRetrieve EDX ; Reread pointers to initialized segments.
    SUB EAX,EAX
    SUB EDX,EDX
    SHR ECX,2
    MOV [%SegmLeft],ECX
    MOV [%TotalSize],EAX
    MOV [%EmitSize],EAX
    MOV [%VirtSize],EAX
    JZ .27:
    ; Compute total virtual size of all initialized segments to EDX:EAX.
.26:MOV EDI,[ESI]
    ADD ESI,4
    ADD EAX,[EDI+SSS.TopLow]
    ADC 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.
    LOOP .26:
.27:BufferCreate [EBX+PGM.Pool],Size=EAX ; Preallocate buffer with total image virtual size.
    MOV [%ReturnEAX],EAX
    JNC .28:
    Msg '9316' ; Allocation error creating image buffer.
    JMP .90:
.28:; Stream the contents of SegmLeft segments to buffer [%ReturnEAX].
    BufferRetrieve [%SegmPtrBuf] ; Reread pointer array to ESI.
    ; Store raw contents of segments in the loop .30: .. .80:.
.30:MOV ECX,[%SegmLeft]
    DEC ECX
    JS .90: ; Quit when no segment is left to stream.
    MOV [%SegmLeft],ECX
    LODSD
    MOV EBX,EAX ; EBX is a segment to stream.
    MOV ECX,[%VirtSize]
    SUB ECX,[%EmitSize]
    JNA .55:
    ; Emitted size of previous segment was less than its virtual size. Align.
    XOR EDX,EDX ; Alignment stuff.
    CMPD [EBX+SSS.Purpose],sssPurposeCODE
    JNE .40:
    MOV EAX,[ESI-8] ; EAX is the previous segment.
    CMPD [EAX+SSS.Purpose],sssPurposeCODE
    JNE .40:
    MOV DL,0x90  ; Alignment stuff is 0x90 only when both segments have PURPOSE=CODE.
.40:; Buffer ECX bytes of alignment stuff DL pending from previous segment.
    BufferStoreByte [%ReturnEAX],EDX
    LOOP .40:
.55:CMPD [%SegmLeft],0
    JNA .60:
    MOV EDI,[ESI] ; Peek the next segment.
    MOV EAX,[EDI+SSS.BottomLow]
    MOV EDX,[EDI+SSS.BottomHigh]
    JMP .65:
.60:; If segment EBX is the last, rather than peek the next one use top of it.
    MOV EAX,[EBX+SSS.TopLow]
    MOV EDX,[EBX+SSS.TopHigh]
.65:; EDX:EAX is now the bottom of the next segment (or top of the last one).
    SUB EAX,[EBX+SSS.BottomLow]
    SBB EDX,[EBX+SSS.BottomHigh]
    JZ .70:
    Msg '8524',EBX ; Size of segment [!1S] exceeded 4 GB.
.70:MOV [%VirtSize],EAX
    PUSH ESI
      BufferRetrieve [EBX+SSS.EmitBuffer]
      MOV [%EmitSize],ECX
      CMP ECX,EAX
      JNA .75:
      XCHG ECX,EAX ; If emitted size is bigger than virtual size, truncate raw data.
.75:  BufferStore [%ReturnEAX],ESI,ECX ; Store raw emitted data.
.80:POP ESI
    JMP .30:
.90:Invoke EaBufferRelease::,[%SegmPtrBuf]
   EndProcedure PgmConcatenateImage
↑ 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, 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 segment, 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, not an import library member.
    ; EDX is the one and only segment.
    MOV ECX,[EDX+SSS.NameSize]
    MOV ESI,[EDX+SSS.NamePtr]
    CMP ECX,5
    JB .90: ; Segment name is too short, not IDATA/IMPORT.
    ; Convert the segment name ESI,ECX to uppercase.
    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:
    ; Segment name contains 'IMPORT' or 'IDATA' and its code is 6..7 bytes long. Module is import library member.
    MOV EBX,[%Pgm]
    SetSt [EBX+PGM.Status],pgmImpLibMember
.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
↑ PgmCheckGlobal Program
PgmCheckGlobal inspects the program if it has some public, exported or imported symbols.
Input
Program is pointer to the inspected PGM.
Output
ZF=1 it the program has no public/exported/imported symbol.
ZF=0 at least one public/exported/imported symbol was found.
Error
-
PgmCheckGlobal Procedure Program
     MOV EBX,[%Program]
     ListGetFirst [EBX+PGM.SymList]
     JZ .90:
.10: JSt [EAX+SYM.Status],symPublic|symExport|symImport,.90: ; Return with ZF=0.
     ListGetNext EAX
     JNZ .10:
.90: EndProcedure PgmCheckGlobal
↑ 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 OMF or COFF libraries will not be combined and linked.

When the base Program format is linkable (OMF,COFF,LIBOMF,LIBCOF) 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 would get linked to an executable format).

When any of the linked program is a standalone module (COFF,OMF) and not a library member (pgmoptLibMember=0), it will be marked pgmSelected regardless if its symbols are referenced or not (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.
    ; Smart linking will be performed when the base program is not linkable (it's an executable image).
    JNSt [EBX+PGM.Pgmopt.Status],pgmoptLinkable,.14:
    ; Otherwise Pass 1 will mark all modules pgmSelected
    ;   when the base program being compiled is a linkable module or library.
    ListGetFirst [EBX+PGM.ModulePgmList]
    JZ .90:                                   ; Nothing to do when no module was loaded.
.10:SetSt [EAX+PGM.Status],pgmSelected
    ListGetNext EAX
    JNZ .10:
    JMP .90:
.14:; Pass 2 collects 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.
    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.
.22:ListGetNext EAX
    JNZ .18:
.26:; Pass 3 inspects each module and flags the module
    ;  as pgmSelected if any of its public symbols is referenced by any symbol in ExtBuf,
    ;  or if the module is standalone COFF|OMF.
    ListGetFirst [EBX+PGM.ModulePgmList]
.30:MOV EBX,EAX ; EBX is now the inspected module.
    JSt [EBX+PGM.Status],pgmSelected,.54:     ; Skip inspection when already referenced.
    JSt [EBX+PGM.Pgmopt.Status],pgmoptLibMember,.34:
    SetSt [EBX+PGM.Status],pgmSelected        ; Select 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:
    ; EDI is public symbol in loaded module EBX. Find if it is referenced.
    BufferRetrieve [%ExtBuf]
    SHR ECX,2
    JZ .58:
.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                          ; The next loaded module.
    JNZ .30:
.58:; Pass 4 collects pointers to global symbols.
    ;  into ExtBuf EDX and PubBuf ECX again (as in Pass 2), 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:
    ; Pass 5 inspects 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:
    ; EDI is public|import symbol in loaded module EBX. Find if it is referenced.
    BufferRetrieve [%ExtBuf]
    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
   ENDPROGRAM pgm

▲Back to the top▲