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

# sym.htm
Class
SYM
Encodings
SymEnc
Macro
SymMemberUpdate
Procedures
SymCombine
SymCreate
SymCreateLiteral
SymCreateSe
SymDelocalName
SymDynamicLink
SymFindByIndex
SymFindByName
SymFindByInterName
SymFrameAddress
SymLowcaseDll
SymReport Unresolved
SymResolve
SymStoreForwarderName

Object SYM represents a program symbol.
All defined symbols of the program including externs, literals and unnamed ($) symbols are kept on PGM.SymList.
A record on PGM.SymList is created

1. when a symbol is defined (explicitly in a label field of statement),
2. when the symbol is referred and wasn't defined yet (in operand field of statement)
3. when a symbol scope is declared with GLOBAL, PUBLIC, EXTERN, EXPORT, IMPORT statement,
4. when a segment is defined (its related symSe is defined implicitly).

Symbol is not created when its attribute is queried, e.g. in `%IF TYPE# aSymbol`.
Basic properties of symbol are
name `.NamePtr, .NameSize`,
scope `.Status`,
offset (AKA value) `.OffsetLow, .OffsetHigh`,
segment identification `.Section`.

When `SYM.Section=0`, it is scalar with value in `SYM.Offset`.
Otherwise it is addressing symbol and its (relocatable) virtual address is calculated as a sum of `SYM.Offset` and VA of the bottom of its segment (even when the symbol is defined in a segment's section).
Section bottom is recalculated when each pass ends, but segment bottom is always 0 at asm-time. When the final assembly pass terminates, sections are permanently linked to their segments and symbol's `SYM.Section` is redefined and then it refers to its segment.
At link-time are homonymous segments from other modules combined, segment order is established by PgmOrderSegments, external symbols resolved and `SYM.Frame` then specifies the group or segment used for addressing and relocation.

#### Symbols and segments binding

In case of scalar (segmentless) symbols some assemblers create an auxilliary pseudosegment named .scalars, absolute etc. When the symbol is external, its segment identification is NULL.

Strategy of €ASM is different: scalar (numeric) symbols have their segment identification empty (`SYM.Section=0`) and for external symbols it creates auxilliar pseudosegments of `sssExtern` type with the same name. Each external symbol in €ASM is accompanied with its homonymous external pseudosegment, which is used in linking.

Also each ordinary relocable section and segment (`[.text], [.data]` etc) is accompanied with its homonymous private symbol (`.text, .data` etc) with `SYM.Status=symSe`, which is used for relocations.

  Standard private or public numeric (absolute) symbol ┌──SYM─────┐ │symPublic │ │.Section=0│ └──────────┘ Standard private or public address symbol ┌─>┌──SYM─────┐ ┌>┌>┌──SSS─────┐ ┌─>┌──SYM─────┐ │ │symPublic │ │ │ │sssSegment│ │ │symSe │ │ │.Section │──┘ │ │.SymPtr │──┘ │.Section │─┐ │ └──────────┘ │ └──────────┘ └──────────┘ │ │ └───────────────────────────────┘ │ │ External unresolved symbol │ │┌>┌──SYM─────┐ ┌──>┌──SSS─────┐ ││ │symExtern │ │ │sssExtern │ ││ │.Section │──┘ │.SymPtr │─┐ ││ └──────────┘ └──────────┘ │ ││ │ │└────────────────────────────────┘ │ │ External symbol resolved by │ matching public symbol by name │ │ ┌──SYM─────┐ ┌──>┌──SSS─────┐ │ │symExtern │ │ │sssExtern │ │ │.Section │──┘ │.SymPtr │─┐ │ └──────────┘ └──────────┘ │ │ │ └─────────────────────────────────┘

Actions performed with all symbols in PseudoENDPROGRAM
Procedure | Condition | Action | Comment
--- | --- | --- | ---
PassCreate |  | SetSt symFixed | At the start of each pass all symbols created in previous passes are marked symFixed.
| RstSt symDefInPass
PassInspect | All symbols are fixed | SetSt pgmLastPass | If one or more symbols is not fixed, the next pass cannot be the last.
!symFixed | RstSt pgmLastPass
PassDestroy | symGlobal && symDefInPass | SetSt symPublic | At the end of each pass the flag symGlobal|symGlobalRef is expanded either to symPublic or symExtern.
symGlobal && !symDefInPass | SetSt symExtern
pgmLastPass && !symExtern && !symDefInPass && !symQueried | E6601
pgmLastPass && !symUsed && !symIncluded && !symPublic && !symExport | W2101

Querying symbol's attributes does not throw error even if the symbol is not defined.

Referencing a symbol which was not defined yet does not throw an error (unless `pgmLastPass` is set). Instead it is created with temporary estimated attribute values:

Initial estimated attributes of forward-referenced private symbol
Attribute | Value | Remark
--- | --- | ---
TYPE# | '?' | Unknown in the first pass(es).
SIZE# | 0 | Assuming it represents a non-dimensional point.
SCOPE# | 'S' | Assuming standard private scope.
OFFSET# | OFFSET#$+64 | Assuming it will be defined later withing short jump reach.
SECTION# | SECTION#$ | Assuming it will be defined in the same section.
SEGMENT# | SEGMENT#$ | Assuming it will be defined in the same segment.
GROUP# | GROUP#$ | Assuming it will be defined in the same group.
PARA# | GROUP#$>>4 | Assuming it will be defined in the same group.

Initial estimated attributes of forward-referenced external or imported symbol
Attribute | Value | Remark
--- | --- | ---
TYPE# | 'A' | Assuming it will stay external (undefined in program).
SIZE# | 0 |
SCOPE# | 'E' | Assuming it will stay external (undefined in program).
OFFSET# | 0 | Runtime offset will be resolved by pseudosegment relocation.
SECTION# | [Symbol] | A new extern pseudosegment is created together with the symbol. Pseudosegment name is identical with external/imported symbol name.
SEGMENT#
GROUP#
PARA #

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

     sym HEAD ; Start of module interface.

↑ SYM

    SYM STRUC          ; +00h.
    .NamePtr       D D ; Full qualified name (leading dot resolved, colons removed), or literal name, e.g.=B'abcd'.
    .NameSize      D D ; Number of bytes in symbol name {without colons).
    .InterNamePtr  D D ; Pointer to imported symbol internal name or to forwarded export name.
    .InterNameSize D D ; Number of bytes in .InterName.
                       ; +10h.
    .OffsetLow     D D ; Value of scalar symbol or offset from the bottom of its section.
    .OffsetHigh    D D ;
    .SymbPtr       D D ; 0 if unresolved, or pointer to the resolved public symbol if SYM.Status=symExtern|symImport|symExport.
    .Section       D D ; 0 for scalar, otherwise ^SSS (sssSection|sssExtern) where it was defined.
                       ; +20h.
    .DllNamePtr    D D ; Pointer to file name of DLL from which the symbol is imported or forwarded DLL name.
    .DllNameSize   D D ; Number of bytes in .DllNamePtr.
    .LinePtr       D D ; Pointer to memory-mapped source text where the symbol was declared.
    .Status        D D ; Symbol properties, see SymEnc below.
                       ; +30h.
    .Size          D D ; Number of bytes emitted or reserved by the declaring statement.
    .Align         D D ; Explicit alignment.   0=default,1,2,4,8,16,...
    .NameIndex     D D ; Ordinal number (0..) in the symbol table. Also used as paragraph FA of module in LIBOMF.
    .NameDynIndex  D D ; Ordinal number (0..) in the dynamic symbol table use in ELFSO format.
                       ; +40h.
    .OrdinalNr     D D ; Imported ordinal number value. Valid when SYM.Status:symImportedByOrd is set.
    ENDSTRUC SYM

↑ SymEnc - symbol encodings
  Some of the `SYM.Status` flags defined here are adopted from the refering statement: `symTypeMask + symPropMask` are kept synchronized with `stmTypeMask + stmPropMask` in statement encoding StmEnc.
  All symbols are granted with `symFixed` in PassCreate (when a new pass starts) and the flag is reset if symbol properties are changed in the pass.

     ; Symbol TYPE#
    symTypeMask       = 0x0000_00FF ; Uppercase letter BUWDQTOYZISNA?. Synchronized with stmTypeMask.
     ; Symbol SCOPE#.       value    scope
    symPrivate        = 0x0000_0000 ; 'S'. Default standard symbol scope.
    symExtern         = 0x0000_0100 ; 'E'. Explicitly declared as EXTERN.
    symImport         = 0x0000_0200 ; 'I'. Explicitly declared as IMPORT.
    symImportedByOrd  = 0x0000_0400 ; 'I'. Imported by ordinal, not by name. Valid with symImport only.
    symExport         = 0x0000_0800 ; 'X'. Explicitly declared as EXPORT.
    symForwarded      = 0x0000_1000 ; 'X'. Export of symbol provided by other DLL. Valid with symExport only
    symSe             = 0x0000_2000 ; 'S'. Implicit private symbol representing the bottom address of a segment|section.
    symEntry          = 0x0000_4000 ; 'P'. Program public Entry symbol. Don't E6601 if undefined in the main program.
    symLiteral        = 0x0000_8000 ; 'S'. Symbol is literal.
    symPublic         = 0x0001_0000 ; 'P'. Explicitly declared as PUBLIC.
    symWeak           = 0x0002_0000 ; 'P'. This weak-public symbol is ignored when another nonweak public symbol is linked.
    symGlobal         = 0x0004_0000 ; 'G'|'P'|'E'. Explicitly declared as GLOBAL.
    symExplScopeMask  = symPublic|symExport|symExtern|symImport|symGlobal|symWeak
     ; Symbol properties.  Synchronized with stmPropMask
    symGlobalRef      = 0x0008_0000 ; EQU stmLabelIsPublic. 'G'|'P'|'E'. Implicitly refered as global (its name ends with ::).
    symIncluded       = 0x0010_0000 ; EQU stmIncluded. Symbol was defined in included source chunk. Do not warn W2101 if not used.
    symProc           = 0x0020_0000 ; EQU stmProc. Symbol is defined as PROC or PROC1 or external function entry.
    symNear           = 0x0040_0000 ; EQU stmNear. Symbol symProc has DIST=NEAR.
    symFar            = 0x0080_0000 ; EQU stmFar.  Symbol symProc has DIST=FAR.
    symPropMask       = symIncluded|symProc|symNear|symFar
    symScopeMask      = symExplScopeMask | symGlobalRef
     ; Referencing symbol flags.
    symEstimated      = 0x0100_0000 ; Offset and other properties are only estimated in this pass.
    symDefined        = 0x0200_0000 ; Symbol was defined at least once in any pass.
    symDefInPass      = 0x0400_0000 ; Symbol was already defined in this pass. Reset in PassDestroy.
    symQueried        = 0x0800_0000 ; Symbol's attribute was queried. Don't E6601 if not defined and not referred.
    symReferenced     = 0x1000_0000 ; Symbol was referred. It should be defined, otherwise E6601.
    symUsed           = 0x2000_0000 ; Symbol was referenced or queried at least once in any pass (symDirty).
    symFixed          = 0x4000_0000 ; Offset and other properties are finally computed and fixed.
    symResolved       = 0x8000_0000 ; This extern|export|import symbol was matched to a public symbol.

      ENDHEAD sym ; End of module interface.

↑ SymFindByName SymScope, NamePtr, NameSize, ProgPtr
  SymFindByName searches for symbol of given scope by its .Name on ProgPgm.SymList.
Input
  SymScope is one of the SymEnc flags `symExtern, symPublic, symImport, symExport`, or `symPrivate=0` which will accept symbol of any scope.
  NamePtr is pointer to the name of searched symbol.
  NameSize is size of the symbol name.
  ProgPtr Pointer to PGM . It may be NULL, current program is searched in this case.
Output
  CF=0
  EAX= Pointer to SYM structure of the symbol.
Error
  CF=1
  EAX= 0 - symbol not found.
Example
  Invoke SymFindByName::, symExtern|symImport,ESI,ECX,0
See also
  SymFindByInterName.
Invoked by
  ExpEvalIdentifier PfDrectveDestroy PfomfLoadModule PfpeOptionalHeader PseudoPROC1 SssCreateStructure SymCombine SymCreate SymCreateLiteral SymCreateSe SymResolve
Invokes
  PgmGetCurrent

    SymFindByName Procedure SymScope, NamePtr, NameSize, ProgPtr
           MOV ESI,[%ProgPtr]
           TEST ESI
           JNZ .10:
           Invoke PgmGetCurrent::
           JC .NotFound:
           MOV ESI,EAX ; ^PGM.
     .10:  MOV EDX,[%NameSize]
           MOV EBX,[%NamePtr]
           TEST EDX
           JZ .NotFound:
           ListGetFirst [ESI+PGM.SymList]
           JZ .NotFound:
     .20:  MOV ECX,[%SymScope]
           JECXZ .40:  ; Skip symbol status check if %SymScope=0 (any scope will fit).
           JNSt [EAX+SYM.Status],ECX,.70:
     .40:  CMP EDX,[EAX+SYM.NameSize]
           JNE .70:
           MOV ESI,[EAX+SYM.NamePtr]
           MOV ECX,EDX
           MOV EDI,EBX
           REPE CMPSB
           JE .Found
     .70:  ListGetNext EAX
           JNZ .20:
    .NotFound:
           SUB EAX,EAX
           STC
    .Found:MOV [%ReturnEAX],EAX
         EndProcedure SymFindByName

↑ SymFindByInterName SymScope, NamePtr, NameSize, ProgPtr
  SymFindByInterName searches for symbol of given scope by .InterName on ProgPgm.SymList. SYM.InterName differs from ordinary (public) SYM.Name only in imported symbols.
Input
  SymScope is one of the SymEnc flags `symExtern, symPublic, symImport, symExport`, or 0, which will accept symbol of any scope.
  NamePtr Name of the searched symbol.
  NameSize Size of symbol internal name.
  ProgPtr Pointer to PGM . It may be NULL, current program is searched in this case.
Output
  CF=0
  EAX= Pointer to SYM structure of the symbol.
Error
  CF=1
  EAX= 0 - symbol not found.
Example
  Invoke SymFindByInterName::,symImport,ESI,ECX,0
See also
  SymFindByName.
Invoked by
  PfomfLoadModule
Invokes
  PgmGetCurrent

    SymFindByInterName Procedure SymScope, NamePtr, NameSize, ProgPtr
           MOV ESI,[%ProgPtr]
           TEST ESI
           JNZ .05:
           Invoke PgmGetCurrent::
           JC .NotFound:
           MOV ESI,EAX ; ^PGM.
     .05:  MOV EDX,[%NameSize]
           MOV EBX,[%NamePtr]
           TEST EDX
           JZ .NotFound:
           ListGetFirst [ESI+PGM.SymList]
           JZ .NotFound:
     .10:  MOV ECX,[%SymScope]
           JECXZ .20:  ; Skip symbol status check if %SymScope=0.
           JNSt [EAX+SYM.Status],ECX,.70:
     .20:  CMP EDX,[EAX+SYM.InterNameSize]
           JNE .70:
           MOV ESI,[EAX+SYM.InterNamePtr]
           MOV ECX,EDX
           MOV EDI,EBX
           REPE CMPSB
           JE .Found
     .70:  ListGetNext EAX
           JNZ .10:
    .NotFound:
           SUB EAX,EAX
           STC
    .Found:MOV [%ReturnEAX],EAX
         EndProcedure SymFindByInterName

↑ SymFindByIndex SymIndex, ProgPtr
  SymFindByIndex will search for the symbol with given index, which is kept as `SYM.NameIndex` in €ASM symbols stored on `PGM.SymList` of the program identified by `%ProgPtr`.
Input
  SymIndex is the ordinal number of the index in ELF|COFF symbol table.
  ProgPtr Pointer to PGM . It may be NULL, current program is searched in this case.
Output
  CF=0
  EAX= Pointer to SYM structure of the symbol.
Error
  CF=1
  EAX= if no such symbols was found or if SymIndex=0.
See also
  SymFindByName.
Invoked by
  PfcoffRelocation2Reloc PfelfLoadPgm
Invokes
  PgmGetCurrent

    SymFindByIndex Procedure SymIndex, ProgPtr
        XOR EAX,EAX
        MOV ECX,[%SymIndex]
        STC
        JECXZ .90:
        MOV EAX,[%ProgPtr]
        TEST EAX
        JNZ .10:
        Invoke PgmGetCurrent::
        JC .90:
    .10:MOV EAX,[EAX+PGM.SymList]
        ListGetFirst EAX
    .30:STC
        JZ .90:
        CMP [EAX+SYM.NameIndex],ECX
        JE .90:
        ListGetNext EAX
        JMP .30:
    .90:MOV [%ReturnEAX],EAX
         EndProcedure SymFindByIndex

↑ SymDelocalName NamePtr, NameSize, NameBuffer, Delocalize
  The identifier Name is checked, delocalized and then stored to NameBuffer. If memberDelocal is set, Name will be prefixed with current namespace when it is .local.
Input
  NamePtr Pointer to the identifier of a symbol or a structure. It may start with dot `.`, but no trailing colons or `[ ]` are allowed.
  NameSize Number of bytes in the name.
  NameBuffer Pointer to the output BUFFER where the checked name will be stored. It should be empty on input.
  Delocalize is one of the flags `memberDelocal, memberDelocalParent` or `memberDelocalNone`.
  - memberDelocal: A name beginning with `.` will be prefixed with the current namespace.
  - memberDelocalParent: A name beginning with `.` will be prefixed with the parent namespace.
  - memberDelocalNone: Leading fullstop `.` is treated as ordinary letter (e.g. segment name `.data`).
  Regardless of Delocalize, the name is tested for permitted characters.
Output
  CF=0, NameBuffer contains valid (delocalized) symbol name.
Error
  CF=1 Errors E6110 reported with macro Msg . Output NameBuffer returns as much of input name as possible.
Invokes
  CtxPeek
Invoked by
  CtxCreate CtxFind ExpEvalIdentifier ExpParseDatatype PseudoPROC1 PseudoSTRUC SymCreate

    SymDelocalName Procedure NamePtr, NameSize, NameBuffer, Delocalize
          MOV ECX,[%NameSize]
          MOV ESI,[%NamePtr]
          MOV EBX,[%NameBuffer]
          MOV EDX,[%Delocalize]
          JECXZ .20:                                 ; Empty name will pass.
          JNSt EDX, memberDelocal | memberDelocalParent, .20: ; If memberDelocalNone.
          CMPB [ESI],'.'                             ; Is the Name local?
          JNE .20:
          ; A local name will be prefixed with the current or parent namespace in the NameBuffer.
          SUB EAX,EAX
          JNSt [%Delocalize],memberDelocalParent,.10:
          Invoke CtxPeek::, ctxNamespace,0           ; Get and forget the current namespace context to EAX.
     .10: Invoke CtxPeek::, ctxNamespace,EAX
          JC .20:                                    ; If there's no namespace on context stack.
          BufferStore EBX,[EAX+CTX.NamePtr],[EAX+CTX.NameSize] ; Store namespace name first.
     .20: BufferStore EBX,ESI,ECX                    ; Store the (local) Name.
          BufferRetrieve EBX
          JECXZ .90:
          LODSB                                      ; Check the first character used in the name.
          DEC ECX
          ExpClassify AL
          TEST AH, expLetter | expFullstop
          JZ .E6110:                                 ; Invalid symbol name "!1S".
     .30: JECXZ .90:
          LODSB
          DEC ECX
          ExpClassify AL
          TEST AH, expLetter | expDigit | expFullstop
          JNZ .30:
    .E6110:LEA EDI,[%NamePtr]
          Msg '6110',EDI,PgmStatus=pgmLastPass       ;  Invalid symbol name "!1S".
          STC
     .90:EndProcedure SymDelocalName

↑ SymMemberUpdate Member
  Macro SymMemberUpdate will copy contents of one class member from the statement to the symbol and clear ECX when they were different.
Input
  Member is the name of homonymous members in STM and SYM: `OffsetLow`, `OffsetHigh`, `Section`, `Size`, `LinePtr`.
  EBX= pointer to STM (source).
  EDI= pointer to SYM (destination).
  ECX= is the constant `symFixed` or 0.
Output
  Symbol refered by EDI is updated from statement refered by EBX.
  EAX=value of `SYM.%Member`.
  ECX= 0 if the members were different, otherwise ECX is unchanged.
Error
  -
Example
  SymMemberUpdate Section
Expanded by
  SymCreate

    SymMemberUpdate %MACRO Member
        MOV EAX,[EBX+STM.%Member]
        CMP EAX,[EDI+SYM.%Member]
        JE .Fixed%Member%.:
        MOV [EDI+SYM.%Member],EAX
        XOR ECX,ECX ; Set flag Fixed to FALSE.
    .Fixed%Member%.:
        %ENDMACRO SymMemberUpdate

↑ SymCreate Reason, NamePtr, NameSize, Statement

  SymCreate returns a program symbol specified by Name. It will create a new symbol if it didn't exist in Program.SymList. The symbol is then updated by Statement properties.
  SymCreate is invoked in those circumstances:

  - When Reason is one of the flags of `symScopeMask`, symbol is declared . This happens when the symbol scope is explicitly declared with pseudoinstruction `GLOBAL`, `PUBLIC`, `EXTERN`, `EXPORT` or `IMPORT`.
  - When Reason is `symDefined`, the symbol is defined . This happens when symbol name appears in the label field of empty or machine instruction or when it is explicitly defined with pseudoinstruction `EQU`, `D`, `PROC` or `PROC1`.
    Flag `symGlobalRef` may be set simultaneously in Reason parameter, the symbol will be marked as symPublic or symExport later.
  - When Reason is `symReferenced`, the symbol is referenced , i.e. its name figures in an expression.
    Again, `symGlobalRef` may be set simultaneously with `symReferenced` in Reason parameter.
  - When Reason is `symQueried`, this means that some attribute of the symbol is queried , i.e. an expression computes its attribute, e.g. `%IF TYPE# Symbol = '?'`. New symbol is not created when it didn't exist yet.

  Type and some other properties of created symbol are provided by STM:`.Section`, `.Offset`, `.Size`, `LinePtr`, `stmTypeMask`,`stmPropMask`.
  Due to optimisation passes the final offset of the created symbol may be different from the value estimated at symbol creation. Statement offset and other properties are updated here. `SYM.Status:symFixed` is reset when symbol properties got changed during the update.

Input
  Reason is a combination of flags in symbol encoding, see above.
  NamePtr is pointer to the symbol name. It can be volatile, untrimmed, colon(s) terminated and local (start with `.`), it will be completed with namespace and permanently stored on Pgm.Pool. It may not be `$`.
  NameSize is number of characters in the symbol name.
  Statement is pointer to STM - parsed statement which concerns the symbol.
  It may be NULL when `Reason=symReferenced`, in this case new symbol will not be created if it didn't exist yet.
Output
  CF=0, EAX= pointer to an updated existing or just created SYM object.
  CF=0, EAX=0 if the reason was symQueried and symbol is undefined.
Error
  CF=1 Errors are reported with macro Msg.
  EAX=0
Depends on
  SymMemberUpdate
Invoked by
  ExpEvalIdentifier IiAssemble IiAssembleMultiop MemberCreate PgmGlobalEntry PseudoData PseudoENDPROC PseudoENDPROC1 PseudoEQU PseudoNoOperation PseudoPROC PseudoPROC1 PseudoScope PseudopcMACRO
Tested by
  t1646 t2381 t2382
Invokes
  EaBufferRelease EaBufferReserve PgmGetCurrent SssCreateExtern SymDelocalName SymFindByName

    SymCreate Procedure Reason, NamePtr, NameSize, Statement
    PgmPtr     LocalVar ; ^Current program.
    PgmStatus  LocalVar ; Local copy of program status.
    Fixed      LocalVar ; Value of SYM.Status:symFixed during SymMemberUpdate.
        ClearLocalVar
        MOV EBX,[%Statement]
        ; Check if Program and Statement is provided.
        TEST EBX
        JZ .05:                              ; Internal error: creating symbol "!1S".
        MOV EDX,[EBX+STM.Program]
        MOV [%PgmPtr],EDX
        TEST EDX
        JZ .05:
        MOV EAX,[EDX+PGM.Status]
        MOV [%PgmStatus],EAX
        MOVD [%Fixed],symFixed
        JMP .10:
    .05:JSt [%Reason],symReferenced,.10:
    .F9960:LEA ESI,[%NamePtr]
        Msg '9960',ESI                       ; Internal error: creating symbol "!1S" outside a statement.
    .Error:
        XOR EAX,EAX
        MOV [%ReturnEAX],EAX
        STC
        JMP .90:
    .E6601: Msg '6601',EDI,[EDI+SYM.LinePtr] ; Symbol "!1S" mentioned at !2@ was not found.
        JMP .Error:
    .E6610:Msg '6610',EDI,[EDI+SYM.LinePtr]  ; Symbol "!1S" was already defined at "!2@"
        JMP .Error:
    .E6621:Msg '6621',EDI,[EDI+SYM.LinePtr]  ; External symbol "!1S" defined at "!2@" cannot be made public.
        JMP .Error:
    .E6622:Msg '6622',EDI,[EDI+SYM.LinePtr]  ; Public symbol "!1S" defined at "!2@" cannot be made external.
        JMP .Error:
    .E6624:Msg '6624',EDI,[EDI+SYM.LinePtr]  ; Symbol "!1S" was declared as external at !2@.
        JMP .Error:
    .E6637:Msg '6637'                        ; Special symbol "$" can be defined with EQU only.
        JMP .Error:
    .E6638:Msg '6638'                        ; Special symbol "$" cannot be declared global.
        JMP .Error:
    .10:TEST EDX
        JNZ .12:
        Invoke PgmGetCurrent::
        MOV EDX,EAX
    .12:TEST EBX
        JNZ .13:
        MOV EBX,[EDX+PGM.CurrentStm]
        TEST EBX
        JZ .F9960:
    .13:; Check symbol name.
        MOV ESI,[%NamePtr]
        MOV ECX,[%NameSize]
        StripSpaces ESI,ECX
        StripColons ESI,ECX
        TEST ECX
        JZ .Error:                           ; Silently do not create symbol with empty name.
        CMP ECX,1
        JNE .15:
        CMPB [ESI],'$'
        JNE .15:
        JSt [%Reason],symScopeMask,.E6638:   ; Special symbol "$" cannot be declared global.
        JMP .E6637:                          ; Special symbol "$" can be defined with EQU only.
    .15:; Convert local name ESI,ECX to standard scope.
        CMPB [ESI],'.'
        JNE .18:                             ; If the name is not local.
        Invoke EaBufferReserve::,SymCreate
        Invoke SymDelocalName,ESI,ECX,EAX,memberDelocal
        BufferRetrieve EAX
        Invoke EaBufferRelease::, EAX
    .18:; ESI,ECX is now nonlocal nonempty trimmed symbol name. Check if it already exists on Program.SymList.
        Invoke SymFindByName,0,ESI,ECX,EDX
        MOV [%ReturnEAX],EAX                 ; Pointer to symbol, if found, otherwise 0.
        MOV EDI,EAX
        JNC .35:                             ; Skip the definition when the symbol already exists.
        ; Symbol does not exist yet. A new symbol will be defined/declared/referenced/queried.
        JSt [%Reason],symQueried,.90:        ; Return no symbol (EAX=0), CF=0.
        ListNew [EDX+PGM.SymList],Zeroed=yes
        MOV EDI,EAX                          ; Pointer to a new empty symbol.
        MOV [%ReturnEAX],EAX
        PoolStore [EDX+PGM.Pool],ESI,ECX
        MOV ESI,EAX                          ; ESI,ECX is now nonvolatile nonlocal symbol name in Pgm.Pool.
        MOV [EDI+SYM.NamePtr],ESI
        MOV [EDI+SYM.NameSize],ECX
        MOV ECX,[%Fixed]
        SymMemberUpdate LinePtr              ; Where was the symbol defined.
        MOV [%Fixed],ECX
        MOV EAX,[%Reason]
        SetSt [EDI+SYM.Status],EAX
        JNSt EAX,symDefined,.20:
        ; New symbol is being defined. Copy its type and properties from the statement.
        MOV ECX,stmTypeMask+stmPropMask+stmLabelIsPublic ; Masks aliases to symbol properties.
        AND ECX,[EBX+STM.Status]
        OR ECX,symDefined+symDefInPass
        SetSt [EDI+SYM.Status],ECX            ; Inherit type, properties and symGlobalRef from the statement.
        RstSt [EDI+SYM.Status],symEstimated
        JMP .45:
    .20:JNSt EAX,symExplScopeMask, .25:
        ; New symbol EDI will be declared by explicit scope pseudoinstruction.
        JSt EAX,symPublic|symExport,.30:      ; Go to estimate its properties.
        ; New symbol is declared symExtern or symImport.
        Invoke SssCreateExtern::,EDI,[%PgmPtr]
        JMP .90:
    .25:JNSt EAX,symReferenced,.90:
        ; New symbol EDI is forward referenced. Its properties will be estimated.
        SetSt EAX,symUsed+symReferenced+symEstimated + '?'
        MOV [EDI+SYM.Status],EAX
    .30:MOV ECX,[%Fixed]
        SymMemberUpdate Section                ; Assume SECTION# $.
        SymMemberUpdate OffsetLow
        SymMemberUpdate OffsetHigh
        MOV [%Fixed],ECX
        ADDD [EDI+SYM.OffsetLow],64            ; Assume OFFSET# $ + 64.
        ADCD [EDI+SYM.OffsetHigh],0
        JMP .80:
    .35: ; Symbol EDI already exists. It is redefined/redeclared/referenced.
        JNSt [%Reason],symDefined,.60:
        ; Existing symbol EDI is (re)defined. It will be updated from the statement EBX.
        MOV EDX,[EDI+SYM.Status]                ; Old properties.
        JSt EDX,symDefInPass, .E6610:           ; Symbol "!1S" was already defined at "!2@".
        JSt EDX,symExtern|symImport, .E6624:    ; Symbol "!1S" was declared as external at !2@.
        MOV EAX,[EBX+STM.Status]                ; New properties.
        MOV ECX,stmTypeMask+stmPropMask+stmLabelIsPublic ; Synchronized with symTypeMask+symPropMask+symGlobalRef.
        JNSt EDX,symEntry,.38:
        OR EAX,stmLabelIsPublic                 ; When the ENTRY is defined in source, pretend it is implicitely global::.
    .38:; Update symbol type and miscellaneous properties.
        JNSt EDX,symGlobalRef,.39:
        SetSt EAX,symGlobalRef
    .39:AND EAX,ECX
        AND EDX,ECX
        CMP EAX,EDX
        JE .40:
        RstSt [EDI+SYM.Status],symFixed         ; If any of symbol properties had changed, a new pass will be required.
    .40:OR EAX,symDefined+symDefInPass
        NOT ECX
        ANDD [EDI+SYM.Status],ECX               ; Erase old symbol type and properties.
        SetSt [EDI+SYM.Status],EAX              ; Replace them with the new ones.
    .45:; Update other properties from the statement.
        MOV ECX,[%Fixed]
        SymMemberUpdate Section
        JNSt [EDI+SYM.Status],symExplScopeMask,.47:
        MOV ECX,[%Fixed]                        ; Do not reset symFixed due to SYM.Section change when the symbol is defined and exported.
    .47:JSt [EDI+SYM.Status],symProc,.48:       ; Size of PROC symbol will be updated in PseudoENDPROC.
        SymMemberUpdate Size
        MOV [%Fixed],ECX
        ; Update offset.
    .48:MOV EAX,[EDI+SYM.OffsetLow]
        MOV EDX,[EDI+SYM.OffsetHigh]
        SUB EAX,[EBX+STM.OffsetLow]
        SBB EDX,[EBX+STM.OffsetHigh]            ; EDX:EAX is negative if symbol offset grows in this pass.
        JS .50:                                 ; Growing is acceptable in any pass, including the fixing one.
        JNSt [%PgmStatus],pgmFixingPass|pgmLastPass,.50:
        ; Do not decrease symbol offset in fixing pass. Stuff the emitted code with NOPs instead.
        ADD [EBX+STM.AlignBytes],EAX
        JMP .80:
    .50:SUB [EDI+SYM.OffsetLow],EAX             ; Update offset in ordinary passes.
        SBB [EDI+SYM.OffsetHigh],EDX
        OR EAX,EDX
        JZ .55:
        RstSt [%Fixed],symFixed                 ; Signalize that offset has changed.
    .55: ; Update flag symFixed.
        JSt [%Fixed],symFixed,.60:
        RstSt [EDI+SYM.Status],symFixed         ; This will force PassInspect to require one more pass.
    .60:MOV EAX,[%Reason]
        JNSt EAX,symExplScopeMask,.80:
        ; Existing symbol EDI is (re)declared. Check for conflicts.
        JNSt EAX,symPublic|symExport,.70:
        JSt EAX,symForwarded|symExport,.70:
        JSt [EDI+SYM.Status],symExtern|symImport,.E6621: ; External symbol "!1S" defined at "!2@" cannot be made public.
    .70:JNSt EAX,symExtern|symImport,.75:
        JSt [EDI+SYM.Status],symDefined,.E6622: ; Public symbol "!1S" defined at "!2@" cannot be made external.
    .75:SetSt [EDI+SYM.Status],EAX              ; Update the scope.
    .80:MOV EAX,[%Reason]
        JNSt EAX,symReferenced,.85:
        ; Existing symbol is referenced.
        SetSt EAX,symUsed
        PUSH EAX,EDI; If symbol EDI is member of a structured symbol, mark the parent symbol symReferenced, too.
          MOV EBX,EDI
          MOV EDI,[EBX+SYM.NamePtr]
          MOV ECX,[EBX+SYM.NameSize]
          MOV EDX,EDI
          MOV AL,'.'
          REPNE SCASB
          JNE .84:                              ; If there's no membership in symbol name.
          DEC EDI
          MOV ECX,[%Statement]
          JECXZ .84:
          MOV ECX,[ECX+STM.Program]
          SUB EDI,EDX                           ; EDX,EDI is now potentional parent's name.
          Invoke SymFindByName,0,EDX,EDI,ECX
          JC .84:                               ; If no parent exists.
          SetSt [EAX+SYM.Status],symUsed+symReferenced
    .84:POP EDI,EAX
        SetSt [EDI+SYM.Status],EAX
        JSt [EDI+SYM.Status],symDefined,.85:
        JNSt [EDI+SYM.Status],symEstimated,.85:
        RstSt [EDI+SYM.Status],symEstimated
        XOR EAX,EAX
        MOV [EDI+SYM.OffsetLow],EAX             ; In the next passes is the original estimated offset $+64 changed to 0.
        MOV [EDI+SYM.OffsetHigh],EAX
    .85:JNSt [%PgmStatus],pgmLastPass,.90:
        ; Final pass check of the symbol.
        JSt [EDI+SYM.Status],symDefined,.88:
        JSt [EDI+SYM.Status],symExtern|symImport|symForwarded|symEntry,.90:
        JMP .E6601:                             ; Symbol "!1S" mentioned at !2@ was not found.
    .88:JSt  [EDI+SYM.Status],symIncluded|symScopeMask|symReferenced|symQueried,.90:
        MOV ECX,[EDI+SYM.Section]
        JECXZ .89:
        JSt [ECX+SSS.Status],sssStructure,.90:
    .89:Msg '2101',EDI                          ; Symbol !1S was defined but never used.
    .90:EndProcedure SymCreate

↑ SymFrameAddress Symbol, Program
  SymFrameAddress returns addressing frame of the Symbol.
  Addressing frame returned in ECX is `sssGroup` if symbol's segment is in a GROUP, otherwise `sssSegment`. In program formats ELF* it is always `sssSegment` because `sssGroup` represents "segment" in ELF program header.
Input
  Symbol is pointer to SYM object.
  Program is pointer to program PGM where the Symbol belongs.
Output
  EDX:EAX= offset of the Symbol.
  ECX= pointer to the group or segment SSS of the symbol. ECX=0 when the symbol is scalar.
Error
  -
Invoked by
  PgmListGlobals

    SymFrameAddress Procedure Symbol, Program
        SUB EAX,EAX
        SUB EDX,EDX
        SUB ECX,ECX
        MOV ESI,[%Symbol]
        MOV EBX,[%Program]
        TEST ESI
        JZ .80:
        MOV EAX,[ESI+SYM.OffsetLow]
        MOV EDX,[ESI+SYM.OffsetHigh]
        MOV ECX,[ESI+SYM.Section]
        JECXZ .80:                   ; Symbol is scalar.
        JSt [EBX+PGM.Pgmopt.Status],pgmoptELFbased,.80: ; Skip group in ELF* format.
        MOV EDI,[ECX+SSS.GroupPtr]
        TEST EDI
        JNZ .10:
        MOV EDI,ECX
    .10:JNSt [ECX+SSS.Status],sssExtern,.20:
        MOV ESI,[ECX+SSS.SymPtr]
        TEST ESI
        JZ .80:
        ADD EAX,[ESI+SYM.OffsetLow]
        ADC EDX,[ESI+SYM.OffsetHigh]
        MOV ECX,[ESI+SYM.Section]
        JECXZ .80:
        MOV ECX,[ECX+SSS.SegmPtr]
        MOV EDI,[ECX+SSS.GroupPtr]
        TEST EDI
        JNZ .20:
        MOV EDI,ECX ; Group EDI equals to segment ECX if the segment is not grouped.
        JMP .30:
    .20:MOV EDI,[EDI+SSS.GroupPtr]  ; Group might be linked from external module.
    .30:TEST EDI
        JNZ .40:
        MOV EDI,ECX ; Group EDI equals to segment ECX if the segment is not grouped.
    .40:; ECX is now symbol's segment and EDI its group, both are nonzero.
        JSt [EBX+PGM.Pgmopt.Status],pgmoptFLAT,.50: ; Frame is 0 in FLAT model.
        ADD EAX,[ECX+SSS.BottomLow]
        ADC EDX,[ECX+SSS.BottomHigh]
        SUB EAX,[EDI+SSS.BottomLow]
        SBB EDX,[EDI+SSS.BottomHigh]
    .50:MOV ECX,EDI
    .80:MOV [%ReturnEAX],EAX
        MOV [%ReturnEDX],EDX
        MOV [%ReturnECX],ECX
    EndProcedure SymFrameAddress

↑ SymDynamicLink Sym, PgmPtr, DllPtr, DllSize, FwdPtr, FwdSize
  This procedure will set symbol's dynamic-linking properties `.DllName` and `.InterName`. Both Dll and Fwd parameters may be empty.
  If the symbol is EXPORT and FwdSize>0, it will set Fwd name to Sym.InterName and make it nonvolatile.
  If the symbol is IMPORT or EXPORT and DllSize>0, it will set Dll name to Sym.DllName and overwrite whatever might have been set in Sym.DllName before. The value is not just a simple copy of DllName, SymDynamicLink tries to find another older import|export symbol with the same DllName and reuse it, so it does not waste memory in program pool. If the name was not on the pool, this procedure will store it.
Input
  Sym is pointer to and exported|imported SYM whose .DllName and .InterName will be set.
  PgmPtr is pointer to PGM where the symbol belongs.
  DllPtr is pointer to a volatile DLL name, e.g. user32.dll.
  DllSize is the number of bytes in the DLL name.
  FwdPtr is pointer to a volatile forwarded symbol name.
  FwdSize is the number of bytes in the forwarded symbol name.
Output
  Dynamic-linking properties of Sym are set.
Invokes
  SssCreateExtern
Invoked by
  PseudoScope

    SymDynamicLink Procedure Sym,PgmPtr,DllPtr,DllSize,FwdPtr,FwdSize
         MOV EDI,[%Sym]
         MOV EBX,[%PgmPtr]
         TEST EDI
         JZ .90:
         Invoke SssCreateExtern::,EDI,EBX
         JSt [EDI+SYM.Status],symImport,.10:
         JNSt [EDI+SYM.Status],symExport,.90:
         SetSt [EDI+SYM.Status],symGlobal ; Export symbol implies globality.
    .10: MOV ECX,[%DllSize]
         MOV ESI,[%DllPtr]
         JECXZ .60: ; If LIB= is not explicitly specified, leave it as is.
         ListGetFirst [EBX+PGM.SymList] ; Reuse identical DllName from any older symbol.
         JZ .40:
    .20: JNSt [EAX+SYM.Status],symImport|symExport,.30:
         MOV EDX,[EAX+SYM.DllNamePtr] ; Old nonvolatile DllName.
         Compare EDX,[EAX+SYM.DllNameSize],ESI,ECX
         JE .50: ; If found, reuse previously stored name EDX.
    .30: ListGetNext EAX
         JNZ .20:
    .40: PoolStore [EBX+PGM.Pool],ESI,ECX ; Make DllName nonvolatile.
         MOV EDX,EAX
    .50: MOV ESI,EDX
         MOV [EDI+SYM.DllNamePtr],ESI
         MOV [EDI+SYM.DllNameSize],ECX
    .60: JNSt [EDI+SYM.Status],symExport,.90:
         MOV ESI,[%FwdPtr]
         MOV ECX,[%FwdSize]
         JECXZ .90: ; If no forward was specified.
         SetSt [EDI+SYM.Status],symForwarded
         PoolStore [EBX+PGM.Pool],ESI,ECX
         JMP .80:
    .70: MOV EAX,[EDI+SYM.NamePtr] ; Default Fwd name is identical with symbol name.
         MOV ECX,[EDI+SYM.NameSize]
    .80: MOV [EDI+SYM.InterNamePtr],EAX
         MOV [EDI+SYM.InterNameSize],ECX
    .90:EndProcedure SymDynamicLink

↑ SymStoreForwarderName Symb, FwdBuffer
  This procedure will store ASCIIZ string which consists of Symb.DllName without path and extension, concatenated with forwarder name from Symb.InterName.
Input
  Symb is pointer to SYM symbol with `symExport+symForwarded` flags set.
  FwdBuffer is pointer to BUFFER where the forwarder name will be stored.
Output
  Dynamic-linking properties of Sym are set.
Error
  -
Invoked by
  PfpeExportCreate

    SymStoreForwarderName Procedure Symb, FwdBuffer
         MOV EBX,[%Symb]
         JNSt [EBX+SYM.Status],symExport,.90:
         JNSt [EBX+SYM.Status],symForwarded,.90:
         MOV ESI,[EBX+SYM.DllNamePtr]
         MOV ECX,[EBX+SYM.DllNameSize]
         FileNameParse ESI,Size=ECX,Unicode=0
         SUB ECX,EAX                           ; EAX,ECX is now the file name without extension.
         MOV EDX,[%FwdBuffer]
         BufferStore EDX,EAX,ECX
         BufferStoreByte EDX,'.'
         MOV ESI,[EBX+SYM.InterNamePtr]
         MOV ECX,[EBX+SYM.InterNameSize]
         TEST ECX
         JNZ .80:
         MOV ESI,[EBX+SYM.NamePtr]
         MOV ECX,[EBX+SYM.NameSize]
    .80: BufferStore EDX,ESI,ECX
         BufferStoreByte EDX,0
    .90:EndProcedure SymStoreForwarderName

↑ SymCreateLiteral LitPtr, LitSize, StmPtr
  SymCreateLiteral will create a new literal symbol on `StmPtr.Program.SymList` or reuse existing symbol with identical name.

  > LIteral strings without type specifier, e.g. `="String"` will be in fact created under modified name `=B"String"` or `=U"String"`.

Input
  LitPtr Source text which should be a literal, i.e. equal sign `=` followed with optional duplication, then with short or long datatype name or structure name, or followed with a quoted string.
  LitSize Number of bytes in source literal definition.
  StmPtr ^STM which refers/creates the literal symbol.
Output
  CF=0
  EAX= Pointer to new or reused SYM.
Error
  CF=1, "E6671 Invalid syntax of literal symbol !1S" should be reported by the caller.
  EAX=0
See also
  ExpParseLiteral.
Invokes
  EaBufferRelease EaBufferReserve ExpAlign ExpEvalData SssCreate@LT SssCreate@RT SssEmit SymFindByName
Invoked by
  ExpEval
Tested by
  t1711 t1713 t1715 t1717

    SymCreateLiteral Procedure LitPtr, LitSize, StmPtr
    LitNameBuffer  LocalVar ; Temporary buffer for updated literal name.
    LitEmitBuffer  LocalVar ; Temporary buffer for emitted data.
    LitRelocBuffer LocalVar ; Temporary buffer for emitted relocations.
    LitSection     LocalVar ; ^SSS with [@RT0] or [@LT*].
    ExpStatus      LocalVar ; Status of literal evaluated to EXP.
         EaStackCheck                                ; Protect from SO in case of recursed literal.
         Invoke EaBufferReserve::,SymCreateLiteral
         MOV [%LitNameBuffer],EAX
         Invoke EaBufferReserve::,SymCreateLiteral
         MOV [%LitEmitBuffer],EAX
         Invoke EaBufferReserve::,SymCreateLiteral
         MOV [%LitRelocBuffer],EAX
         MOV EBX,[%StmPtr]
         TEST EBX
         JZ .Error:
         MOV EDX,[EBX+STM.Program]
         TEST EDX
         JNZ .10:
    .Error:SUB EAX,EAX
         MOV [%ReturnEAX],EAX
         STC
         JMP .90:
    .10: ; Prepare literal name.
         MOV EDI,[%LitNameBuffer]
         MOV ESI,[%LitPtr]
         MOV ECX,[%LitSize]
         StripSpaces ESI,ECX
         BufferStore EDI,ESI,ECX
         LEA EDX,[ESI+ECX]
         LODSB
         CMP AL,'='
         JNE .Error:
    .14: CMP ESI,EDX
         JNB .22:
         LODSB
         ExpClassify AL
         TEST AH,expWhiteSpace
         JNZ .14:
         DEC ESI                                     ; ESI now points to source data expression.
         SUB EDX,ESI                                 ; EDX is size of the value, i.e. everything following =.
         TEST AH,expQuote
         JZ .22:
         ; Literal defines a string without type. B or U will be injected into its name.
         BufferClear EDI
         MOV AX,"=U"
         JSt [Ea.Eaopt.Status::],eaoptUNICODE,.18:   ; If EUROASM UNICODE=Enabled.
         MOV AX,"=B"
    .18: BufferStoreWord EDI,EAX                     ; Insert either =B or =U.
         BufferStore EDI,ESI,EDX                     ; Insert the rest (quoted string).
    .22: ; Evaluate literal data to %LitEmitBuffer and %LitRelocBuffer.
         BufferRetrieve EDI                          ; Literal name, e.g. =U"Text".
         INC ESI                                     ; Omit theleading =.
         DEC ECX
         MOV EDX,[%LitEmitBuffer]
         Invoke ExpEvalData::,EDX,[%LitRelocBuffer],ESI,ECX,0,EBX
         MOV [%ExpStatus],EAX                        ; EAX='B' or 'W' or 'I'...etc (datatype) + expString flag.
         JC .Error:
         JNSt EAX,expString, .30:
         CMP AL,'B'
         JNE .26:
         BufferStoreByte EDX,0                       ; Terminate string literal value with NUL byte.
    .26: CMP AL,'U'
         JNE .30:
         BufferStoreWord EDX,0                       ; Terminate string literal value with NUL unichar.
    .30: CMP AL,'I'                                  ; Get|create literal section suitable for the literal data|code.
         JNE .34:
         Invoke SssCreate@RT::,0,EBX                 ; Create code literal section if it didn't exist.
         JMP .38:                                    ; EBX=^STM.
    .34: Invoke SssCreate@LT::,EAX,EBX               ; Create data literal section if it didn't exist.
    .38: JC .Error:
         MOV [%LitSection],EAX
         BufferRetrieve [%LitNameBuffer]
         MOV EDX,[EBX+STM.Program]
         XOR EDI,EDI
         Invoke SymFindByName,0,ESI,ECX,EDX          ; Look if literal symbol with verbatim same name was already created in program EDX.
         MOV [%ReturnEAX],EAX                        ; EAX=^SYM or 0.
         JNC .40:
         PoolStore [EDX+PGM.Pool],ESI,ECX             ; Not found. A new literal symbol with name ESI,ECX will be created in program EDX.
         MOV ESI,EAX                                  ; ESI,ECX is now nonvolatile literal name, e.g. "=B 1+2", with PGM lifetime.
         ListNew [EDX+PGM.SymList], Zeroed=yes        ; Allocate room for the new literal symbol in program EDX.
         MOV [%ReturnEAX],EAX                         ; EAX=^SYM, EBX=^STM, EDX=^PGM, ESI,ECX=literal name.
         MOV [EAX+SYM.NamePtr],ESI
         MOV [EAX+SYM.NameSize],ECX
         MOV ECX,[%ExpStatus]                         ; (Re)initialize status of literal symbol EAX.
         MOV EDX,[EBX+STM.LinePtr]
         RstSt ECX,expString
         SetSt ECX,symLiteral+symDefined+symReferenced+symUsed+symFixed
         MOV [EAX+SYM.LinePtr],EDX                    ; Source line where it was first referred, i.e. created.
         MOV [EAX+SYM.Status],ECX
    .40: MOV EBX,[%LitSection]
         MOV EDI,[EAX+SYM.OffsetLow]
         JSt [EAX+SYM.Status],symDefInPass,.62:       ; Skip emit to literal section when already emitted.
         SetSt [EAX+SYM.Status],symDefInPass
         ; Look if the value of symbol EAX stored in [%LitEmitBuffer] is already emitted in %LitSection.
         BufferRetrieve [%LitRelocBuffer]             ; Are there any relocations in literal value?
         TEST ECX                                     ; Relocatable value cannot be shared
         JNZ .53:                                     ;  because it might be resolved to a different VA.
         BufferRetrieve [EBX+SSS.EmitBuffer]
         MOV EDI,ESI
         LEA EDX,[ESI+ECX]                            ; EDI..EDX is now the already emitted contents of literal section.
    .46: BufferRetrieve [%LitEmitBuffer]              ; ESI,ECX is now the new literal value. EAX=^SYM, EBX=^SSS.
         LEA EAX,[EDI+ECX]
         CMP EAX,EDX
         JA  .53:                                     ; Skip when behind the section limit.
         PUSH EDI
           REPE CMPSB
         POP EDI
         JE .49:                                      ; Reusable literal value was found.
         ADD EDI,[EBX+SSS.Alignment]                  ; Try the next possible aligned position in literal section.
         JMP .46:
    .49: ; Reusable literal value was found in SSS.EmitBuffer at EDI.
         BufferRetrieve [EBX+SSS.EmitBuffer]
         SUB EDI,ESI
         JMP .62:

    .53: ; Literal value was not found in SSS.EmitBuffer. Let's emit [%LitEmitBuffer] to the section EBX.
         Invoke ExpAlign::,[EBX+SSS.OrgLow],[EBX+SSS.Alignment],0 ; Get AlignBytes to ECX.
         MOV EDI,[EBX+SSS.OrgLow]
         ADD EDI,ECX
         MOV EAX,[%ReturnEAX]
         MOV [EAX+SYM.OffsetLow],EDI
         MOV ESI,[%StmPtr]
         MOV EAX,[ESI+STM.Program]
         MOV ESI,[%LitRelocBuffer]
         JSt [EAX+PGM.Status],pgmLastPass, .57:
         XOR ESI,ESI                                  ; Do not store relocation if not in the final pass.
    .57: Invoke SssEmit::,EBX,[%LitEmitBuffer],ESI,ECX
    .62: MOV EAX,[%ReturnEAX]                         ; Update properties of literal symbol EAX.
         CMP EDI,[EAX+SYM.OffsetLow]                  ; EDI is symbol offset from segment's bottom.
         JE .70:
         MOV [EAX+SYM.OffsetLow],EDI                  ; Offset from previous pass is different.
         RstSt [EAX+SYM.Status],symFixed
    .70: MOV EBX,[%LitSection]
         CMP EBX,[EAX+SYM.Section]
         JE .75:
         MOV [EAX+SYM.Section],EBX
         RstSt [EAX+SYM.Status],symFixed
    .75: MOV ECX,[EBX+SSS.Alignment]
         CMP ECX,[EAX+SYM.Align]
         JE .80:
         MOV [EAX+SYM.Align],ECX
         RstSt [EAX+SYM.Status],symFixed
    .80: BufferRetrieve [%LitEmitBuffer]
         CMP ECX,[EAX+SYM.Size]
         JE .90:
         MOV [EAX+SYM.Size],ECX
         RstSt [EAX+SYM.Status],symFixed
    .90: PUSHFD                                       ; Keep CF.
          Invoke EaBufferRelease::,[%LitEmitBuffer]
          Invoke EaBufferRelease::,[%LitRelocBuffer]
          Invoke EaBufferRelease::,[%LitNameBuffer]
         POPFD                                        ; Restore CF.
        EndProcedure SymCreateLiteral

↑ SymCreateSe SePtr, PgmPtr
  SymCreateSe will create or update a symbol of type `symSe` with the same name as the group|segment|section. This new symbol represents the bottom of group|segment|section and it can be used in relocations.
Input
  SePtr ^SSS of type sssGroup or sssSegment or sssSection.
  PgmPtr ^PGM where the symbol belongs.
Output
  Symbol symSe is created or updated and its pointer stored in SePtr's `SSS.SymPtr`.
Error
  -
Invokes
  SymFindByName
Invoked by
  PfelfSssTab PfelfsoSssDynTab PfomfLoadModule PgmGroupByModel PseudoGROUP SssCreateImplicit SssCreateSe

    SymCreateSe Procedure SePtr, PgmPtr
           MOV EDI,[%SePtr]
           MOV EBX,[%PgmPtr]
           MOV ESI,[EDI+SSS.NamePtr]
           MOV ECX,[EDI+SSS.NameSize]
           Invoke SymFindByName,symSe,ESI,ECX,EBX
           JNC .20:
           ListNew [EBX+PGM.SymList],Zeroed=Yes
    .20:   MOV [EDI+SSS.SymPtr],EAX
           MOV [EAX+SYM.NamePtr],ESI
           MOV [EAX+SYM.NameSize],ECX
           MOV [EAX+SYM.Section],EDI
           MOV ECX,[EDI+SSS.LinePtr]
           MOV [EAX+SYM.Status],symSe+symDefined+symFixed+'A'
           MOV [EAX+SYM.LinePtr],ECX
        EndProcedure SymCreateSe

↑ SymCombine Symbol, BasePgm
  SymCombine will copy the global Symbol from linked module to the BasePgm.
Input
  Symbol is pointer to a symbol in selected module, which is being merged to BasePgm.
  BasePgm is pointer to a PGM.
Output
  -
Error
  -
Invokes
  SymFindByName
Invoked by
  PgmCombine

    SymCombine Procedure Symbol, BasePgm
        MOV EBX,[%BasePgm]
        MOV ESI,[%Symbol]
        MOV EDX,[ESI+SYM.Status]
        JNSt EDX,symSe,.30:
        MOV ECX,[ESI+SYM.Section]             ; Symbol symSe (which is assigned to a segment).
        JECXZ .90:
        JNSt [ECX+SSS.Status],sssCombined,.30:
        MOV EDX,[ECX+SSS.SegmPtr]             ; Do not copy symSe symbol from already combined segment to the BasePgm.
        MOV EAX,[EDX+SSS.SymPtr]              ; Let it refer to the symSe in BasePgm instead.
        MOV [ESI+SYM.SymbPtr],EAX
        JMP .90:
    .30:JNSt EDX,symPublic,.40:
        Invoke SymFindByName, symPublic,[ESI+SYM.NamePtr],[ESI+SYM.NameSize],EBX
        JC .70:
        Msg '8540',EAX,[EAX+SYM.LinePtr]; Public symbol "!1S" was already defined at !2@.
        JMP .90:

    .40:JNSt EDX,symImport,.70:
        Invoke SymFindByName, symImport,[ESI+SYM.NamePtr],[ESI+SYM.NameSize],EBX
        JC .70:
        ; Merge import symbol ESI to EAX.
        MOV [ESI+SYM.SymbPtr],EAX
        SetSt [ESI+SYM.Status],symResolved
        MOV ECX,[EAX+SYM.DllNameSize]
        TEST ECX
        JNZ .90:
        MOV EDI,[ESI+SYM.DllNamePtr]
        MOV ECX,[ESI+SYM.DllNameSize]
        MOV [EAX+SYM.DllNamePtr],EDI
        MOV [EAX+SYM.DllNameSize],ECX
        JMP .90:

    .70:ListStore [EBX+PGM.SymList],ESI       ; Copy the linked symbol to BasePgm.
        MOV EDI,EAX                           ; Copied symbol on BasePgm.
        SetSt [EDI+SYM.Status],symDefined
        MOV [ESI+SYM.SymbPtr],EDI             ; Let the abandoned symbol ESI refer to copied symbol EDI.
        MOV ECX,[EDI+SYM.Section]
        JECXZ .90:
        MOV EAX,[ECX+SSS.BottomLow]
        MOV EDX,[ECX+SSS.BottomHigh]
        MOV ECX,[ECX+SSS.SegmPtr]
        SUB EAX,[ECX+SSS.BottomLow]
        SBB EDX,[ECX+SSS.BottomHigh]
        ADD [EDI+SYM.OffsetLow],EAX
        ADC [EDI+SYM.OffsetHigh],EDX
        MOV [EDI+SYM.Section],ECX
    .90:EndProcedure SymCombine

↑ SymReportUnresolved PgmPtr

  PgmCheckUnresolved will check all external and imported symbols and report E6961 when they were not matched to their homonymous public symbols.

  This procedure is not invoked when the program format is linkable.

Input
  PgmPtr is pointer to the PGM (program whose symbols are checked).
Output
  -
Error
  E6961 is reported with macro Msg for each unresolved symbol.
Invoked by
  PfbinCompile PfbootCompile PfcomCompile PfmzCompile PfpeCompile
Tested by
  t2381

    SymReportUnresolved Procedure PgmPtr
        MOV EBX,[%PgmPtr]
        ListGetFirst [EBX+PGM.SymList]
        JZ .90:
    .10:JNSt [EAX+SYM.Status],symExtern|symImport,.80:
        JSt [EAX+SYM.Status],symResolved|symPublic|symForwarded,.80:
        Msg '6961',EAX                               ; Unresolved external/imported symbol "!1S".
    .80:ListGetNext EAX
        JNZ .10:
    .90:EndProcedure SymReportUnresolved

↑ SymResolve Symbol, BasePgm
  Combining linked modules often ends with multiple symbols with the same name in BasePgm. SymResolve will match one unresolved external, exported and imported symbol (which was copied to the BasePgm) with homonymous public or weak symbol. Succesfully matched symbol is then marked with `symResolved` and its `SYM.SymbPtr` points to the matching public (or weak) symbol in BasePgm.
Input
  Symbol is pointer to a SYM.
  BasePgm is pointer to a PGM which it is linked to.
Output
  -
Error
  -
Invokes
  SymFindByName
Invoked by
  PgmSymResolve

    SymResolve Procedure Symbol, BasePgm
        MOV EDI,[%Symbol]
        MOV EBX,[%BasePgm]
        JSt [EDI+SYM.Status],symResolved,.90:
        JNSt [EDI+SYM.Status],symExtern|symImport,.90:
        MOV EDX,EDI
        MOV ECX,[EDI+SYM.SymbPtr]
        JECXZ .30:
        MOV EDX,ECX
        MOV ECX,[EDX+SYM.SymbPtr]
        JECXZ .30:
        MOV EDX,ECX
    .30:MOV ESI,[EDI+SYM.NamePtr]
        MOV ECX,[EDI+SYM.NameSize]
        Invoke SymFindByName,symPublic,ESI,ECX,EBX   ; Prefer PUBLIC scope.
        JNC .50:
        Invoke SymFindByName,symWeak,ESI,ECX,EBX     ; When PUBLIC symbol is not found, try to find a WEAK one.
        JNC .50:
        Invoke SymFindByName,symImport,ESI,ECX,EBX   ; Try to match external symbol to an imported one.
        JNC .50:
        Invoke SymFindByName,symSe,ESI,ECX,EBX       ; Try to match external segment's pseudosymbol to a standard one.
        JC .90:
    .50:CMP EAX,EDI
        JE .90:                                      ; Never resolve symbol with itself.
        MOV [EDI+SYM.SymbPtr],EAX                    ; Pointer to the resolved public counterpart.
    .60:SetSt [EDI+SYM.Status],symResolved
    .70:JNSt [EDI+SYM.Status],symExport,.90:
        SetSt [EAX+SYM.Status],symExport
    .90:EndProcedure SymResolve

↑ SymLowcaseDll ImpSym
  Name of DLL, where is the symbol imported from, will be converted to lower case.
Input
  ImpSym is pointer to an (imported) SYM.
Output
  Filename specified by `SYM.DllNamePtr` is converted to lower case.
Error
  -
Invoked by
  PfpeImportCreate

    SymLowcaseDll Procedure ImpSym
        MOV EBX,[%ImpSym]
        MOV ECX,[EBX+SYM.DllNameSize]
        JECXZ .90:
        MOV ESI,[EBX+SYM.DllNamePtr]
    .20:LODSB
        CMP AL,'A'
        JB .50:
        CMP AL,'Z'
        JA .50:
        XOR AL,'A'^'a'
        MOV [ESI-1],AL
    .50:DEC ECX
        JNZ .20:
    .90:EndProcedure SymLowcaseDll

    ENDPROGRAM sym

▲Back to the top▲