EuroAssembler Index Manual Download Source Macros

Sitemap Links Forum Tests Projects


This is a common source for generating EuroAssembler output file in a chosen program format. Its interface and procedures are used in actual format modules (pfbin.htm, pfcom.htm etc).

Program formats modules
FormatPlatformModule file
allLinker for all €ASM format output filespf.htm
BINBinary output filepfbin.htm
COFF16|32|64bit Common Object Format modulepfcoff.htm
COM16bit DOS executablepfcom.htm
DLL32|64bit Dynamically Linked Librarypfdll.htm
LIBCOFLibrary of COFF modulespflibcof.htm
LIBOMFLibrary of OMF modulespflibomf.htm
MZ16bit DOS executablepfmz.htm
OMF16|32bit Object Module Formatpfomf.htm
PE32|64bit Windows Portable Executablepfpe.htm
RSRCCompiled Windows resource (input only)pfrsrc.htm

Most important procedure PfOutput is invoked after the final assembly pass and it provides all functions of linker:

  1. Sections of the just assembled main program are joined to their base segment,
  2. external programs (modules and libraries) requested in source text by pseudoinstruction LINK are loaded from disk, converted to internal PGM format and saved on main program's .ModulePgmList,
  3. segments of all loaded programs are combined to the main program,
  4. order of segments is set,
  5. segments are linked into executable image,
  6. headers and other auxilliary structures are updated,
  7. data in requested output file format are stored to output STREAM and then to a disk file.
Linker object model
PseudoENDPROGRAM PgmLinkSections PassInspect ; Final pass detected. PgmListLiterals PassDestroy ; The final assembly pass is ending. PgmDestroy ; The base program assembly is ending. ChunkTotalLines PgmCheckDirty PfOutput PgmLinkSections pgmLinking ; Subsections no longer exist, they are linked to their segment. SssLinkSegment SssLinkSection PfLoad ; Read modules specified by LINK statements. SysOpenFileMap PfDetect ; Detect input-format. PgmoptSetLinkProp PfFormatLoadPgm ; Convert linked module to PGM class. PfomfLoadModule PgmoptSetLinkProp PfDrectveDestroy PgmDetectImportModule PfcoffLoadModule PgmoptSetLinkProp PfDrectveDestroy PgmSelectModules ; Select referenced modules (smart linking). PgmCombine ; Combine segments and symbols of selected modules to the base PGM. SssCombine RelocCombine PfFormatCompile ; Convert PGM to the output format and store to a stream. PgmCreateImplicitGroups PfmzDefaultStack RelocSort PfpeImportCreate PfpeExportCreate PfpeBaserelocCreate PfrsrcLoadIconFile PgmOrderSegments PgmLinkImage ; Concatenate segments to an image. SssResizeGroup SymResolveImage ; Match extern and public symbols. RelocResolveImage ; Resolve relocations in image. PfDrectveCreate PfcoffFileHeader PfpeOptionalHeader PfpeImportFixup PfpeExportFixup PfpeBaserelocFixup PfcoffSegmRawData PgmEvalEntry PfSuboperate SysCreateFile SysWriteFile ; Flush the memory stream to a disk file. PgmListMap PgmListGlobals CtxDiscard
In order to introduce a new output program format:
  1. Choose a unique format shortcut and add it on PfList.
  2. Create source file pfshortcut.htm with the corresponding formatting procedure PfshortcutCompile.
  3. Update procedure PfDetect.
  4. Update procedure PgmoptSetDefaults.
  5. Update procedure PgmoptSetLinkProp.
  6. Update procedure PgmCreateImplicitSegments.
  7. Update procedure PgmListMap.
  8. Update PROGRAM FORMAT= documentation in manual.
  9. Create test file(s) for the new format.
  10. Rebuild EuroAssembler.
 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
 pf HEAD ; Start module interface.
↑ %PfList
This enumeration declares output program formats which EuroAssembler supports.
For each supported format it also defines a corresponding numeric option in PgmoptEncoding:
pgmoptBIN EQU 1
pgmoptCOM EQU 2
pgmoptOMF EQU 3
etc. Names of theese values are enumerated in %PfPgmoptList.

%value %SETA 1     ; Numeric value of PGMOPT.Status pgmoptBIN, pgmoptCOM etc.
%PfPgmoptList %SET ; Initialize the enumeration to emptiness.
pf %FOR %PfList
     pgmopt%pf     EQU %value ; The actual option definition.
     %PfPgmoptList %SET pgmopt%pf,%PfPgmoptList ; Prepend the option name.
     %value        %SETA %value+1 ; Increment value for the next format.
   %ENDFOR pf
↑ PfQueryChar Char
Macro PfQueryChar searches for a %Char in string ESI,EDX.
Char is a character to find.
ESI is pointer to the searched string.
EDX is size of the searched string.
ZF=1 if the string contains Char.
EAX,EDI,ECX changed.
PfQueryChar %MACRO Char ; Ad hoc macro to find a Char in string ESI,EDX.
       MOV AL,%Char
       MOV EDI,ESI
       MOV ECX,EDX
     %ENDMACRO PfQueryChar
 ENDHEAD pf  ; End of module interface.
↑ PfOutput Pgm
Procedure PfOutput creates output program file with name specified by Pgm.Pgmopt.OutFile in the format specified by Pgm.Pgmopt.Status:pgmoptFormatMask.
PfOutput is dispatcher to actual format creators PfFormat Compile, which write the output to an intermediary stream.
Pgm Pointer to a completely assembled PGM object.
Output file is written to disk.
CF=1 Errors are reported with macro Msg.
DictSearchByData PfLoad PfSuboperate PgmCombine PgmLinkSections PgmSelectModules SssResizeGroup
Invoked by
PfOutput Procedure Pgm
OutputStream LocalVar ; Pointer to a STREAM.
OutputFile   LocalVar Size=SIZE#FILE
    MOV EBX,[%Pgm]
    SetSt [EBX+PGM.Status],pgmLinking
    Invoke PgmLinkSections::,EBX ; Join and then discard all subsections of program segments.
    ; Load all external modules which were requested by pseudoinstruction LINK
    ;  and store them on Pgm.ModulePgmLst as objects of PGM class.
    MOV ECX,[EBX+PGM.LinkFilesNr]
    JECXZ .20: ; If no link requested.
    MOV ESI,[EBX+PGM.LinkFileNamesTable]
    MOV EDI,[EBX+PGM.LinkLinePtrTable]
    Invoke PfLoad,EBX,EAX,[EDI]
    ADD EDI,4
    LOOP .10:
.20:Invoke PgmSelectModules::,EBX
    ; Combine segments and symbols from loaded programs to the base program EBX.
    ; If the target program is a library (LIBOMF or LIBCOF), loaded modules are not combined.
    JSt [EBX+PGM.Pgmopt.Status],pgmoptLibrary,.25:
    Invoke PgmCombine::,EBX
    ; Update groups' .Bottom and .Top after segments have been combined in base program.
.25:ListGetFirst [EBX+PGM.SssList]
    JZ .33:
.31:JNSt [EAX+SSS.Status],sssGroup,.32:
    Invoke SssResizeGroup::,EAX,EBX
.32:ListGetNext EAX
    JNZ .31:
.33:; Prepare memory stream for output file.
     StreamCreate [EBX+PGM.Pool],BufSize=32K
     MOV [%OutputStream],EAX
     JC .90:
     ; Get the name of procedure for output file formating: PfbinCompile, PfcomCompile etc.
     ; Due to file-naming conventions, the format name (BIN,COM, OMF...)
     ;    needs to be coverted to lower case at asm-time.
     MOV ECX,[EBX+PGM.Pgmopt+PGMOPT.Status]
     AND ECX,pgmoptFormatMask
  PF %FOR %PfList
       CMP ECX,pgmopt%PF
       JNE .Not%PF:
       ; Convert program-format shortcut %PF to lowercase %pf, e.g. OMF to omf.
  %PFn %SETS %PF    ; Set number of characters in shortcut to %PFn.
  %pf  %SET         ; Initialize lowcase shortcut to emptiness.
     i %FOR 1..%PFn
         %lc %SETC "%PF[%i]" | 0x20 ; Convert %i-th character to lowercase.
         %pf %SET %pf%lc
       %ENDFOR i
       Invoke Pf%pf{}Compile::,EAX,EBX ; Perform the actual formating, e.g. PfomfCompile.
       JMP .35:
     JMP .90:
 .35:Invoke PfSuboperate,EBX,[%OutputStream]
     MOV [%OutputStream],EAX
     ; Warn when the output file already exists on the Src.PfList.
     MOV ESI,[EBX+PGM.Pgmopt.OutFilePtr]
     MOV ECX,[EBX+PGM.Pgmopt.OutFileSize]
     ListGetFirst [Src::+SRC.PfList]
     JZ .65:
 .40:CMP ECX,[EAX+4]
     JNE .60: ; If filename sizes do not match.
     MOV EDI,[EAX+0] ; EDI is now pointer to 1st character of filename.
 .45:  MOV AL,[ESI]
       MOV AH,[EDI]
       INC ESI
       INC EDI
       CMPB [Ea::+EA.EuroasmOS],'W'
       JNE .50: ; Skip if running on case-sensitive filesystem.
       OR AX,0x2020 ; Simplified conversion to lowercase.
 .50:  CMP AL,AH
       JNE .55:
       LOOP .45:
     JNE .60:
     Msg '3990',EAX ; Overwriting previously generated output file "!1S".
     JMP .70:
 .60:ListGetNext EAX
     JNZ .40:
 .65:ListNew [Src::+SRC.PfList] ; Add the filename to Src.PfList .
     MOV [EAX+0],ESI ; Store filename ESI,ECX to the new leaf.
     MOV [EAX+4],ECX
 .70:; Flush the OutputStream contents to OutputFile on disk.
     LEA EDI,[%OutputFile]
     Clear EDI,Size=SIZE#FILE
     SysCreateFile EDI,ESI ; Assign OutputFile EDI with filename ESI and open it for writing.
     JC .E7951: ; Report error if the file is not writable.
     StreamDump [%OutputStream], .WriteBlock: ; Write the entire stream contents to OutputFile.
.WriteBlock:PROC1 ; Callback for writing the stream contents block ESI,ECX to [%OutputFile].
     LEA EDI,[%OutputFile]
     SysWriteFile EDI,ESI,ECX
     JNC .OK:
 .E7951:LEA ECX,[EDI+FILE.Name] ; Expects EDI=^FILE, EAX=error code, CF=1.
     Msg '7951',EAX,ECX ; Error 0x!1H writing to output file "!2$".
    ENDP1 .WriteBlock:

 .80:; File is written, now prepare I0660 with information about output file.
     LEA EBX,[EDI+FILE.Name]
     PUSH [EDI+FILE.Size]
      SysCloseFile EDI
      MOV EAX,[%Pgm]
      LEA EDX,[EAX+PGM.Pgmopt]
      MOV EAX,[EDX+PGMOPT.Status]
      AND EAX,pgmoptModelMask
      Invoke DictSearchByData::,DictProgramModels::,EAX
      MOV EDI,ESI ; !2S
      MOV EAX,[EDX+PGMOPT.Status]
      AND EAX,pgmoptFormatMask
      Invoke DictSearchByData::,DictProgramFormats::,EAX ; ESI=!3S
      MOV ECX,pgmoptWidthMask
      AND ECX,[EDX+PGMOPT.Status]
      SAR ECX,20 ; Program width ECX=!1D.
     POP EAX ; Output file size !5D.
     MOV EDX,[%Pgm]
     JSt [EDX+PGM.Status],pgmEnvelope,.I0760:
     Msg '0660',ECX,EDI,ESI,EBX,EAX ; !1Dbit !2S !3S file "!4$" created, size=!5D.
     JMP .90:
.E7951:CALL .WriteBlock.E7951: ; Though the file EDI is not writtable,
     JMP .80:                  ;    report I0660 (it will have size=0).
.I0760:Msg '0760',ECX,EDI,ESI,EBX,EAX ; !1Dbit !2S !3S file "!4$" created from source, size=!5D.
.90:EndProcedure PfOutput
↑ PfLoad Pgm, Filemask, LinePtr
PfLoad will resolve linked object module name(s) specified by (wildcarded) Filemask, check their format, and convert each linked file to a PGM structure created on Pgm.Pool. The new created PGM then will be added to Pgm.ModulePgmList.
Pgm is pointer to a completely assembled PGM object to which it is linked (base program).
Filemask is pointer to zero-terminated linked file name. It may contain path and wildcards.
LinePtr points to the source line with LINK statement which requested the file. It is used in error messages.
Requested program(s) is loaded and stored to Pgm.ModulePgmList (but not linked yet).
CF=1 Errors are reported with macro Msg.
See also
Invoked by
EaoptGetOnePath PfDetect PgmoptSetLinkProp
PfLoad Procedure Pgm, FilemaskPtr, LinePtr
LinkFile      LocalVar Size=SIZE#FILE ; Linked file.
FilemaskEnd   LocalVar
SubopPtr      LocalVar
SubopEnd      LocalVar
PathNr        LocalVar
Status        LocalVar ; Resolving status, see the flags below.
%AtLeast1     %SETA 1  ; One or more files were resolved from wildcards.
%Wildcarded   %SETA 2  ; Filemask contains * or ?.
%WithPath     %SETA 4  ; Filemask contains slash or colon.
    MOV ESI,[%FilemaskPtr] ; Parse the name of linked file.
    GetLength$ ESI
    StripSpaces ESI,ECX
    LEA EDX,[ESI+ECX] ; First suppose no quotes and no suboperations, e.g. file*.obj.
    MOV [%FilemaskEnd],EDX
    MOV [%SubopPtr],EDX
    MOV [%SubopEnd],EDX ; Suppose no suboperation.
    CMP AL,'"'
    JNE .10:
    MOV EDI,ESI ; Filemask is in quotes.
    MOV [%FilemaskPtr],EDI
    REPNE SCASB ; Search for the terminating double quote.
    Msg cc=NE,'6951',ESI ; Wrong linked file name !1$.',0
    JNE .90:
    MOV [%SubopPtr],EDI
    MOV [%FilemaskEnd],EDI
.10:; [%FilemaskPtr]..[%FilemaskEnd] now specifies unquoted filename, perhaps with path and wildcards, without suboperations.
     MOV ESI,[%FilemaskPtr]
     MOV EDX,[%FilemaskEnd]
     ; Query if wildcarded (contains asterix or question mark).
     RstSt [%Status],%Wildcarded + %AtLeast1
     PfQueryChar '*'
     JE .20:
     PfQueryChar '?'
     JNE .30:
.20: SetSt [%Status],%Wildcarded
.30: ; Query if Filemask was specified with path (if it contains slash or colon).
     SetSt [%Status],%WithPath
     PfQueryChar '\'
     JE .55:
     PfQueryChar '/'
     JE .55:
     PfQueryChar ':'
     JE .55:
     RstSt [%Status],%WithPath
     ; If no path was specified in LINK statement, we must try all pathes from %^LINKPATH.
.40: Invoke EaoptGetOnePath::,[Ea::+EA.Eaopt.LinkPathPtr],[Ea::+EA.Eaopt.LinkPathSize],[%PathNr] ; Get the path to ESI,ECX.
     JC .80: ; If no more path specified in LINKPATH=.
     INCD [%PathNr] ; Prepare for the next path.
     MOV EDX,[%FilemaskEnd]
     SUB EDX,[%FilemaskPtr] ; EDX is now size of filemask without path.
     LEA EAX,[EDX+ECX] ; ESI,ECX is one include path.
     Msg cc=A,'6953',ESI ; Size of LinkPath "!1_" + size of filename exceeded 256 characters.
     JA .90:
     LEA EDI,[%LinkFile+FILE.Name] ; Assign path+filemask to LinkFile.
     MOV AX,'\/'
     CMPB [Ea::+EA.EuroasmOS],'W' ; Choose slash or backslash.
     JE .50:
     XCHG AL,AH ; Backslash if euroasm.exe runs on Windows, otherwise use slash /.
.50: CMP AL,[EDI-1]
     JE .60:
     CMP AH,[EDI-1]
     JE .60:
     STOSB ; If the path was not terminated with slash or backslash, append it.
     JMP .60:
.55: LEA EDI,[%LinkFile+FILE.Name]
.60: MOV ESI,[%FilemaskPtr]
     SUB EAX,EAX ; Zero terminate filemask.
     STOSB ; LinkFile is now assigned with path and filemask.
     LEA EDI,[%LinkFile]
     SysEachFile EDI, .File ; Perform callback .File with each wildcard-resolved filename.
.File:PROC1 ; Callback from SysEachFile. Input:
        ; EBX=^FILE with assigned FILE.Name.
        ; EDX=find-handle from OS.
        ; ESI=^FILE.Name
        ; EDI=WIN32_FIND_DATAW
     SetSt [%Status],%AtLeast1
     LEA EDX,[EBX+FILE.Name]
     SysOpenFileMap EBX,EDX
     Msg cc=C,'8530',EDX ; Error reading linked file "!1$".
     JC .F8:
     MOV ECX,EAX ; The contents of linked file EBX is now mapped in memory ESI,ECX.
     ; Detect format of linked file.
     Invoke PfDetect, ESI,ECX ; Returns one of DictProgramFormats in EAX.
     Msg cc=Z,'8539',EDX ; Format of file "!1$" was not recognized.
     JZ .F8:
     MOV EDI,EAX ; Pointer to Dict_Format.
     Invoke PgmoptSetLinkProp::,[EDI+DICT.Data] ; Set properties corresponding to the format.
     TEST EAX,pgmoptLinkable | pgmoptImportable
     Msg cc=Z,'8534',EDI,EDX ; Format !1S of file "!2$" is not linkable.
     JZ .F8:
     Msg '0560',EDI,EDX ;  Linking !1S module "!2$".
   ; Procedures for input file loading are PfomfLoadPgm, PfcoffLoadPgm, PfdllLoadPgm etc.
   ; Due to naming conventions we need lowercase format shortcut.
     MOV EAX,[EDI+DICT.Data]
     AND EAX,pgmoptFormatMask
  PF %FOR %PfList
       CMP AL,pgmopt%PF
       JNE .Not%PF:
       ; Convert program-format shortcut %PF to lowercase %pf, e.g. OMF to omf.
  %PFn %SETS %PF    ; Set number of characters in shortcut to %PFn.
  %pf  %SET         ; Initialize lowcase shortcut to emptiness.
     i %FOR 1..%PFn
         %lc %SETC "%PF[%i]" | 0x20 ; Convert %i-th character to lowercase
         %pf %SET %pf%lc            ;  and append to the loading-procedure name.
       %ENDFOR i
       Invoke Pf%pf{}LoadPgm::,[%Pgm],ESI,ECX,EDX ; Perform the actual loading, e.g. PfcoffLoadPgm.
       JMP .F8:
 .F8:SysCloseFile EBX ; Release the memory-mapped file.
     ENDP1 .File:
     JSt [%Status],%WithPath,.80:
     JSt [%Status],%Wildcarded,.40: ; Continue search with the next link path.
     JNSt [%Status],%AtLeast1,.40:  ; If non-wildcarded file not found, continue search.
     JMP .90:
.E6954:LEA EAX,[EDI+FILE.Name]
     Msg '6954',EAX ; Linked file "!1$" not found.
     JMP .90:
.80: JSt [%Status],%Wildcarded,.90: ; Do not report "E6954 not found" when specified with wildcards.
     JNSt [%Status],%AtLeast1,.E6954: ; Linked file "!1$" not found.
.90:EndProcedure PfLoad
↑ PfDetect InputObjPtr, InputObjSize
PfDetect will inspect and recognize the format of input file submitted to linker.
InputObjPtr is pointer to the file contents mapped in memory, loaded by the caller.
InputObjSize is number of bytes in the file.
ZF=0, EAX= is pointer to DICT dictionary item of recognized DictProgramFormats, e.g. Dict_FormatCOFF.
ZF=1, EAX=0 when none of known program linkable formats was recognized.
See also
Invoked by
PfomfLoadRecord PfrsrcStoreRecord
PfDetect Procedure InputObjPtr, InputObjSize
       MOV EBX,[%InputObjPtr]
       MOV ECX,[%InputObjSize]
       LEA EDX,[EBX+ECX] ; InputObjEnd.
        ; Try format RSRC. It consists of PFRSRC_RES_HEADER records+data.
       MOV EAX,EBX
.RSRC1:Invoke PfrsrcStoreRecord::,EAX,EDX,0,0 ; Silently verify one resource header and raw data.
       JC .NoRSRC:
       JNZ .RSRC1: ; Test the next resource record at EAX.
       MOV EAX,Dict_FormatRSRC:: ; Valid format RSRC detected.
       JMP .Detected:
       ; Try format LIBCOF. It starts with signature !<arch>.
       CMP ECX,8
       JB .NoLIBCOF:
       MOV EAX,0x72613C21 ; '!<ar'
       CMP [EBX],EAX
       JNE .NoLIBCOF:
       MOV EAX,0x0A3E6863 ; 'ch>',0x0A
       CMP [EBX+4],EAX
       JNE .NoLIBCOF
       MOV EAX,Dict_FormatLIBCOF::
       JMP .Detected:
       ; Try format LIBOMF. It consists of valid OMF records,
       ; the first one is LIBHDR.
       CMP ECX,27
       JB .NoLIBOMF: ; If too short.
       MOV ESI,EBX
       JNE .NoLIBOMF:
       MOVZXW EDI,[ESI+1]
       ADD EDI,3 ; EDI is now library page size. Legal sizes are 16,32,64,..32K.
       TEST EDI,0x0000_000F
       JNZ .NoLIBOMF: ; If invalid page size.
       PUSH ECX
        BSF EAX,EDI
        BSR ECX,EDI
        CMP EAX,ECX
       POP ECX
       JNE .NoLIBOMF: ; If invalid page size (not power of two).
       CMP EAX,4
       JB .NoLIBOMF: ; If invalid page size.
       CMP EAX,15
       JA .NoLIBOMF: ; If invalid page size.
       DEC EDI ; Page size is OK. EDI is now the align mask for modules in library.
       MOV EDX,[ESI+3] ; File address of library dictionary.
       ADD EDX,ESI ; EDX now points to the dictionary, i.e. end of library records.
.LIBOMF1:Invoke PfomfLoadRecord::,ESI,EBX,EDX,0 ; Silently verify one OMF record.
       JC .NoLIBOMF:
       MOV AL,[ESI]
       AND AL,~1 ; Reset LSbit (when MODEND32 is used).
       JNE .LIBOMF3:
       ; Some linkers rather than blowing up the length of MODEND prefer to keep MODEND short
       ; and page-align the room between MODEND and the following THEADR/LHEADR.
       LEA EAX,[ESI+ECX]
       SUB EAX,EBX ; EAX is now FA of the end of MODEND.
       NEG EAX
       AND EAX,EDI ; 0..PageSize-1.
       ADD ESI,EAX ; Align ESI to the next page.
.LIBOMF3:ADD ESI,ECX ; ESI now points to the next OMF record.
       TEST ECX
       JNZ .LIBOMF1: ; Check the next OMF record.
       MOV EAX,Dict_FormatLIBOMF::
       JMP .Detected:
       ; Try format OMF. If consists of valid OMF records.
       CMP ECX,10
       JB .NoOMF: ; If too short.
       MOV ESI,EBX
       MOV AL,[ESI]
       CMP AL,THEADR ; The first OMF record must be THEADR or LHEADR.
       JE .OMF1:
       JNE .NoOMF:
.OMF1: Invoke PfomfLoadRecord::,ESI,EBX,EDX,0 ; Silently verify one OMF record.
       JC .NoOMF:
       ADD ESI,ECX ; ESI now points to the next OMF record.
       TEST ECX
       JNZ .OMF1: ; Check the next OMF record.
       MOV EAX,Dict_FormatOMF::
       JMP .Detected:
       ; Try format COFF. It begins with valid machine type.
       MOV ECX,[%InputObjSize]
       JNA .NoCOFF: ; If too short.
       MOV AX,[EBX]
       Dispatch AX,pfcoffFILE_MACHINE_I386, pfcoffFILE_MACHINE_I486, \
                   pfcoffFILE_MACHINE_I586, pfcoffFILE_MACHINE_IA64, \
                   pfcoffFILE_MACHINE_AMD64, pfcoffFILE_MACHINE_UNKNOWN
       JMP .NoCOFF:
.pfcoffFILE_MACHINE_UNKNOWN: ; Linked file format detected as COFF.
        MOV EAX,Dict_FormatCOFF::
        JMP .Detected:         ; todo more check on COFF validity?
       ; Try formats PE, DLL, MZ. They begin with MZ signature.
       JB .NONE: ; If too short.
       CMPW [EBX],'MZ'
       JNE .NONE:
       MOV EAX,Dict_FormatMZ:: ; It may be MZ format.
       MOV ESI,[EBX+PFMZ_DOS_HEADER.e_lfanew] ; Offset to PE signature.
       ADD ESI,EBX
       CMP ESI,EDX
       JA .Detected: ; Not PE, but MZ.
       CMPD [ESI],'PE'
       JNE .Detected:; Not PE, but MZ.
       ADD ESI,4 ; Skip the PE signature to file header.
       CMP EDI,EDX
       JA .Detected: ; Not PE, but MZ.
       MOV EAX,Dict_FormatDLL:: ; File is PE or DLL.
       JSt [ESI+PFCOFF_FILE_HEADER.Characteristics],pfcoffFILE_DLL,.Detected:
       MOV EAX,Dict_FormatPE::
       JMP .Detected:
.NONE: XOR EAX,EAX ; File format was not recognized.
       MOV [%ReturnEAX],EAX
       TEST EAX ; Set ZF.
    EndProcedure PfDetect
↑ PfDrectveCreate Program

PfDrectveCreate creates new auxilliary section of unspecified width with name [.drectve] and PURPOSE=DRECTVE when a non-executable files is compiled, if a section with that name and purpose does not exist yet. Otherwise it appends to its contents.
The section contains linker directives which will be used when the final executable is created.

See PfDrectveDestroy for the list of supported directives.

The section is not created if dynamic linking is not required, i.e. when no symbol with scope EXPORT or IMPORT was defined in the Program.

New segment [.drectve] is created in Program.
See also
Invoked by
EaBufferRelease EaBufferReserve SssCreate SssFind
PfDrectveCreate Procedure Program
TextBuf   LocalVar ; Pointer to a BUFFER with section contents.
Statement LocalVar Size=SIZE# STM ; Fake statement required by SssCreate.
    MOV EBX,[%Program]
    Invoke EaBufferReserve::,PfDrectveCreate
    MOV [%TextBuf],EAX
    ; Create directive /ENTRY: if this Program specifies entry symbol.
    MOV ECX,[EBX+PGM.Pgmopt.EntrySize]
    JECXZ .10: ; If no ENTRY= was specified.
    MOV ESI,[EBX+PGM.Pgmopt.EntryPtr]
    BufferStore EDX,=B" /ENTRY:",8
    BufferStore EDX,ESI,ECX
.10:; Create directives /EXPORT: and /IMPORT: if such symbols exist.
    ListGetFirst [EBX+PGM.SymList]
    JZ .60:
.20:JNSt [EAX+SYM.Status],symExport,.30:
    BufferStore EDX,=B" /EXPORT:",9
    BufferStore EDX,[EAX+SYM.NamePtr],[EAX+SYM.NameSize] ; Exported symbol name.
.30:JNSt [EAX+SYM.Status],symImport,.50:
    BufferStore EDX,=B" /IMPORT:",9
    BufferStore EDX,[EAX+SYM.NamePtr],[EAX+SYM.NameSize] ; Imported symbol name.
    MOV ECX,[EAX+SYM.DllNameSize]
    MOV ESI,[EAX+SYM.DllNamePtr]
    JECXZ .50:
    CMP ECX,12
    JNE .40: ; Nondefault DLL.
    MOV EDI,0x20202020
    MOV ECX,[ESI+0]
    OR ECX,EDI ; Convert letters to lower case.
    CMP ECX,'kern'
    JNE .40: ; Nondefault DLL.
    MOV ECX,[ESI+4]
    OR ECX,EDI ; Convert letters to lower case.
    CMP ECX,'el32'
    JNE .40: ; Nondefault DLL.
    MOV ECX,[ESI+8]
    OR ECX,EDI ; Convert letters to lower case.
    CMP ECX,'.dll'
    JE .50: ; If symbol EAX is imported from default library "%EaDefaultDllName", which may be omitted.
.40:; Nondefault DLL is used. Store its colon-separated quoted name.
    BufferStoreWord EDX,':"'
    BufferStore EDX,[EAX+SYM.DllNamePtr],[EAX+SYM.DllNameSize]
    BufferStoreByte EDX,'"'
.50:ListGetNext EAX
    JNZ .20: ; The next symbol.
.60:BufferRetrieve EDX
    JECXZ .80: ; Skip if no directive was stored.
    ; Section [.drectve] will be created.
    LEA EDI,[%Statement]
    MOV [EDI+STM.Program],EBX
    MOV EAX,[EBX+PGM.CurrentStm]
    JZ .70:
    MOV EAX,[EAX+STM.LinePtr]
    MOV [EDI+STM.LinePtr],EAX
.70:Invoke SssFind::,sssSegment,sssPublic,=B'.drectve',8,EBX
    JNC .75: ; If [.drectve] exists, reuse it.
    Invoke SssCreate::,EDI,0,=B'.drectve',8,sssSegment+sssNotBSS+sssPublic,sssPurposeDRECTVE,0
.75:BufferClear [EAX+SSS.EmitBuffer]
    BufferStore [EAX+SSS.EmitBuffer],ESI,ECX
    MOV [EAX+SSS.TopLow],ECX
    RstSt [EAX+SSS.Status],sssWidthMask ; [.drectve] segment has unspecified width.
.80:Invoke EaBufferRelease::,[%TextBuf]
.90:EndProcedure PfDrectveCreate
↑ PfDrectveDestroy Program

PfDrectveDestroy will find an auxiliary segment [.drectve] with PURPOSE=DRECTVE, read, parse and assemble its emitted contents, and then it will discard the segment.

Typical directive looks like /name: value. Directive consists of

slash / or hyphen -
colon : or equal sign = or white space(s)
quoted or unquoted string.
DictLookup SssCreateExtern SssFind SymFindByName
Invoked by
PfcoffLoadModule PfomfLoadModule

Directives are separated with comma , and/or white space. String of directives may begin with prefix UTF-8 BOM (DB 0xEF,0xBB,0xBF). Unsupported directives are silently ignored.

Linker directives supported by €ASM:
/IMPORT:ImportedSymbolName:"ImportDllName" . ImportDllName may be omitted when it equals to kernel32.dll .

Directive /IMPORT is not supported by MS linker. €ASM uses /IMPORT: to propagate import-properties of symbols imported by COFF object module to the main executable. Thanks to this, the executable does not necessarily have to import those symbols again.
In order to support a new MS linker directive by €ASM:
  1. add the directive name to dictionary DictDrectve
  2. create its public handler in this procedure, e.g. PfDrectveDestroy.NEWOPTION::
Program is a pointer to the just loaded PGM.
Each linker directive created or updated its symbol in the Program.
Segment [.drectve] was removed from Program.SssList.
CF=1 Errors are reported with macro Msg.
See also
Invoked by
Tested by
PfDrectveDestroy Procedure Program
DrectveSegm LocalVar ; ^SSS [.drectve].
DllNameSize LocalVar ; Components parsed from the format /NAME:Value:"DllName".
DllNamePtr  LocalVar
ValueSize   LocalVar 
ValuePtr    LocalVar
NameSize    LocalVar
NamePtr     LocalVar
    MOV EBX,[%Program]
    Invoke SssFind::,sssSegment,0,=B'.drectve',8,EBX
    JC .90:
    JNSt [EAX+SSS.Purpose],sssPurposeDRECTVE,.90:
    MOV [%DrectveSegm],EAX
    BufferRetrieve [EAX+SSS.EmitBuffer]
    LEA EDX,[ESI+ECX] ; ESI..EDX are now unparsed linker directives.
.10:; Search for prefix of directive /Name. It starts with / or -.
    JNB .80:
    CMP AL,'/'
    JE .20:
    CMP AL,'-'
    JNE .10:
.20:MOV [%NamePtr],ESI
    MOV [%ValuePtr],EAX
    MOV [%ValueSize],EAX
    MOV [%DllNamePtr],EAX
    MOV [%DllNameSize],EAX
    ; Search for suffix which terminates the /Name (colon, equal or space).
    JNB .40:
    ExpClassify AL
    CMP AL,':'
    JE .35:
    CMP AL,'='
    JE .35:
    CMP AH,expWhiteSpace
    JNE .30:
    SUB ECX,[%NamePtr]
    MOV [%NameSize],ECX
    INC ESI ; Skip the suffix.
.45:; Search for the value. It may be in quotes.
    JNB .75:
    ExpClassify AL
    CMP AH,expWhiteSpace
    JE .45:
    CMP AH,expQuote
    JNE .55:
    MOV AH,AL ; Value is in single or double quotes AL=AH.
    MOV [%ValuePtr],ESI
    JNB .10: ; Silently abandon the invalid value because it is not properly terminated.
    CMP AL,AH ; Is it the terminating quote?
    JNE .50:
    SUB ECX,[%ValuePtr]
    MOV [%ValueSize],ECX ; Netto size without quotes.
    JMP .75:
.55:DEC ESI ; Back to the first nonwhite value character.
    MOV [%ValuePtr],ESI ; Value is unquoted.
    JNB .70: ; End of value found.
    CMP AL,',' ; Unquoted comma terminates the value.
    JE .65:
    ExpClassify AL
    CMP AH,expWhiteSpace
    JNE .60:
.65:DEC ESI ; Unquoted comma or space terminated the value.
    SUB ECX,[%ValuePtr]
    MOV [%ValueSize],ECX
.75:; Name and value parsed succesfully.
    Invoke DictLookup::,DictDrectve::,[%NamePtr],[%NameSize]
    JC .10: ; Abandon when the name is not among supported directives.
    ; EAX is now the directive handler, e.g. PfDrectveDestroy.EXPORT::.
      LEA ECX,[%NamePtr]
      LEA EDX,[%ValuePtr]
      Msg '0563',ECX,EDX ; Accepting link directive /!1S:!2S.
      MOV ESI,[%ValuePtr]
      MOV ECX,[%ValueSize]
      CALL EAX ; Execute the handler.
    JMP .10:
.80:ListRemove [EBX+PGM.SssList],[%DrectveSegm] ; Discard the [.drectve] segment.
    JMP .90:

; Directive handlers. They may destroy any GPR but EBP. Input:
                             ; EBX=^PGM,
                             ; ESI,ECX=Value,
                             ; EBP=local variables frame.
.ENTRY:: ; Handler of directive /ENTRY:EntrySymbolName.
         ; It declared executable entry point inside the linked module.
    PoolStore [EBX+PGM.Pool],ESI,ECX
    MOV [EBX+PGM.Pgmopt.EntryPtr],EAX
    MOV [EBX+PGM.Pgmopt.EntrySize],ECX

.EXPORT:: ; Handler of directive /EXPORT:ExportedSymbolName. It marks the symbol as symExport.
          ; It is used to declare exportness of a symbol inside the linked module.
    Invoke SymFindByName::,0,ESI,ECX,EBX
    JNC .X5:
    LEA EDX,[%ValuePtr]
    Msg '6120',EDX ; Symbol "!1S" not found.
.X5:SetSt [EAX+SYM.Status],symExport

.IMPORT:: ; Handler of directive /IMPORT:ImportedSymbolName:"DllName".
          ; It creates/updates the symbol as symIMPORT.
    JECXZ .I9:
    MOV EDI,ESI ; Beginning of value.
    LEA EDX,[ESI+ECX] ; End of value.
    JNB .I3:
    CMP AL,':' ; Search for the colon which terminates ImportedSymbolName.
    JE .I2:
    CMP AL,'='
    JNE .I1:
    SUB ECX,EDI ; EDI,ECX is now ImportedSymbolName.
    Invoke SymFindByName::,0,EDI,ECX,EBX
    JNC .I4:
    ; Create imported symbol named EDI,ECX in program EBX.
    ListNew [EBX+PGM.SymList],Zeroed=yes
    MOV [EAX+SYM.NamePtr],EDI
    MOV [EAX+SYM.NameSize],ECX
    XCHG EAX,EDI ; EAX is now volatile symbol name, EDI=^SYM.
      PoolStore [EBX+PGM.Pool],EAX,ECX
      MOV [EDI+SYM.NamePtr],EAX
.I4:MOV EDI,EAX ; EDI is now the imported symbol.
    SetSt [EDI+SYM.Status],symImport   ;;  +symUsed
    Invoke SssCreateExtern::,EDI,EBX ; Accompany the import with its extern pseudosegment.
    JNB .I7: ; If no explicit DllName follows.
    ; ImportedSymbolName is followed with colon and DllName in double quotes.
    LODSB ; Skip the : or =.
    JNB .I7: ; No explicit DllName follows.
    MOV AH,AL ; Double quote expected.
    CMP AL,'"'
    JNE .I7: ; Treat invalid DllName as default "kernel32.dll".
    MOV [EDI+SYM.DllNamePtr],ESI
    JNB .I7: ; Treat invalid DllName as default "kernel32.dll".
    JNE .I5:
    SUB ESI,[EDI+SYM.DllNamePtr]
    MOV [EDI+SYM.DllNameSize],ESI
    JZ .I7:
    PoolStore [EBX+PGM.Pool],[EDI+SYM.DllNamePtr],ESI,ZeroTerminate=YES
    MOV [EDI+SYM.DllNamePtr],EAX ; Make DllName persistent.
    JMP .I9:
.I7:; No valid DllName provided, assume "kernel32.dll".
    MOV [EDI+SYM.DllNamePtr],=B"%EaDefaultDllName"
    MOV [EDI+SYM.DllNameSize],%EaDefaultDllNameSize
.90:EndProcedure PfDrectveDestroy
↑ PfSuboperate Program, OutStream
Procedure PfSuboperate resolves suboperations specified in program option, such as PROGRAM OUTFILE="file.bin"[256..]. If no suboperation is requested, or if the OUTFILE= is left empty (default), this procedure does nothing.
Otherwise it will trim the OutStream and also remove suboperators from Program.Pgmopt.OutFile specification.
Program Pointer to a completely assembled PGM object.
OutStream is pointer to STREAM which contains the complete assembled and linked output file.
CF=1 Errors are reported with macro Msg.
Invoked by
Tested by
t9121 t9122 t9123
EaBufferRelease EaBufferReserve ExpConvertToNumber ExpEval ExpParseRange ExpParseSuboperation VarExpandField
PfSuboperate  Procedure Program, OutStream
PfSopOutFileSize LocalVar ; Filename size with removed suboperations prepared for return.
PfSopOutFileEnd  LocalVar ; ^Behind the last closing bracket.
PfSopStart       LocalVar ; ^Opening bracket of one suboperation.
PfSopEnd         LocalVar ; ^Behind the closing bracket of one suboperation.
PfSopLeftPtr     LocalVar ; ^Left range expression.
PfSopLeftEnd     LocalVar ; ^Behind the left range expression.
PfSopRightPtr    LocalVar ; ^Right range expression.
PfSopRightEnd    LocalVar ; ^Behind the right range expression.
PfSopLeftVal     LocalVar ; Left range value.
PfSopRightVal    LocalVar ; Right range value.
PfSopStatus      LocalVar ; Bit 1 is set if the range covers both left and right value.
PfSopBuffer      LocalVar ; Temporary buffer for range calculation.
PfSopStream      LocalVar ; Temporary substream for suboperation.
PfSopExp         LocalVar Size=SIZE# EXP
    MOV EAX,[%OutStream]
    MOV EBX,[%Program]
    MOV [%ReturnEAX],EAX ; Prepare for the case when no suboperation is required.
    Invoke EaBufferReserve::,PfSuboperate
    MOV [%PfSopBuffer],EAX
    MOV EDI,[EBX+PGM.Pgmopt.OutFilePtr]
    MOV ECX,[EBX+PGM.Pgmopt.OutFileSize]
    MOV [%PfSopOutFileSize],ECX
    LEA EDX,[EDI+ECX] ; End of suboperated filename (behind the last closing bracket).
    MOV [%PfSopOutFileEnd],EDX
    ; Leading quote of filename was already removed, ESI,ECX may be e.g.file.bin"{5..%&-4}[256..].
    MOV AL,'"'
    JNE .90: ; If no valid suboperation.
    MOV [%PfSopStart],EDI ; EDI now should point to the opening bracket following the quote. Otherwise E8572.
    LEA EAX,[EDI-1]
    MOV [%PfSopOutFileSize],EAX ; Size with removed suboperations prepared for return.
.10: ; EDI..EDX must be a suboperations chain, e.g. {5..%&-4}[256..], or empty.
    JNB .90: ; If empty chain, we're done.
    Invoke ExpParseSuboperation::,EDI,EDX ; Returns ESI at the end of 1st suboperator (closing bracket).
    JC .E8572: ; Invalid suboperation of PROGRAM OUTFILE="!1S. Ignored.
    ; EDI..ESI is now one valid suboperator, e.g. {5..%&-4}.
    MOV [%PfSopEnd],ESI
    DEC ESI ; Strip off the brackets.
    RstSt [%PfSopStatus],1
    Invoke ExpParseRange::,EDI,ESI ; Returns EAX behind the range operator.
    MOV [%PfSopLeftPtr],EDI
    MOV [%PfSopRightPtr],EAX
    MOV [%PfSopRightEnd],ESI
    JC .20: ; If no range operator (double point) was found in EDI..ESI. Returned EAX=ESI.
    SetSt [%PfSopStatus],1
    ; EAX points behind the range operator ...
    SUB EAX,2 ; Skip the range operator.
.20:MOV [%PfSopLeftEnd],EAX ; Both range expressions are parsed now.
    MOVD [%PfSopLeftVal],1 ; Default.
    StreamReset [%ReturnEAX]
    CMPB [EDI-1],'{'
    JE .30:
    StreamGetSize [%ReturnEAX]
    JMP .32:
.30:StreamGetLines [%ReturnEAX]
.32:MOV [%PfSopRightVal],EAX ; Default.
    LEA EDX,[%PfSopExp]
    BufferClear [%PfSopBuffer] ; Evaluate the left range value.
    Invoke VarExpandField::,[%PfSopLeftPtr],[%PfSopLeftEnd],[%PfSopBuffer],EAX ; Expand %&.
    BufferRetrieve [%PfSopBuffer]
    StripSpaces ESI,ECX
    JECXZ .38: ; Leave empty left range at default=1.
    Invoke ExpEval::,EDX,ESI,ECX,0
    JC .E8572:
    Invoke ExpConvertToNumber::,EDX
    JC .E8572:
    MOV [%PfSopLeftVal],ECX
    JMPS .40:
.38:SetSt [%PfSopStatus],1
.40:BufferClear [%PfSopBuffer] ; Evaluate the right range value.
    Invoke VarExpandField::,[%PfSopRightPtr],[%PfSopRightEnd],[%PfSopBuffer],EAX
    BufferRetrieve [%PfSopBuffer]
    StripSpaces ESI,ECX
    JECXZ .45: ; Leave empty right range at default=%&=[%PfSopRightVal].
    Invoke ExpEval::,EDX,ESI,ECX,0
    JC .E8572:
    Invoke ExpConvertToNumber::,EDX
    JC .E8572:
    MOV [%PfSopRightVal],ECX
    JMP .50:
.45:JSt [%PfSopStatus],1,.50:
    MOV ECX,[%PfSopLeftVal]
    MOV [%PfSopRightVal],ECX
.50:; Both range values are calculated now.
    StreamCreate [EBX+PGM.Pool]
    MOV [%PfSopStream],EAX
    ; Copy suboperated stream [%ReturnEAX] to the new [%PfSopStream].
    StreamReset [%ReturnEAX]
    SUB EDX,EDX ; Line counter.
    MOV ECX,[%PfSopLeftVal]
    JNL .70:
    CMPB [EDI-1],'{'
    JE .65:
    StreamReadByte [%ReturnEAX] ; Substring - skip 1 byte.
    JMP .60:
.65:StreamReadLn [%ReturnEAX],0 ; Sublist - skip 1 line.
    JMP .60:
.70:CMP EDX,[%PfSopRightVal]
    JG .85:
    CMPB [EDI-1],'{'
    JE .75:
    StreamReadByte [%ReturnEAX] ; Substring - read 1 byte.
    StreamStoreByte [%PfSopStream],AL
    JMP .70:
.75:BufferClear [%PfSopBuffer]
    StreamReadLn [%ReturnEAX],[%PfSopBuffer] ; Sublist - read 1 line.
    BufferRetrieve [%PfSopBuffer]
    StreamStore [%PfSopStream],ESI,ECX
    JMP .70:
.85: ; [%PfSopStream] is suboperated now. It will replace current stream at [%ReturnEAX].
    MOV EAX,[%PfSopStream]
    MOV [%ReturnEAX],EAX
    MOV EDI,[%PfSopEnd]
    MOV EDX,[%PfSopOutFileEnd]
    JMP .10: ; Suboperators may chain.
.E8572:LEA EAX,[EBX+PGM.Pgmopt.OutFilePtr]
     Msg '8572',EAX ; Invalid suboperation of PROGRAM OUTFILE="!1S. Ignored.
.90:Invoke EaBufferRelease::,[%PfSopBuffer]
    MOV EAX,[%PfSopOutFileSize]
    MOV EDI,[EBX+PGM.Pgmopt.OutFilePtr]
    MOV [EBX+PGM.Pgmopt.OutFileSize],EAX ; Remove suboperations from outfile name.
    MOVB [EDI+EAX],0 ; Zero terminate the filename.
   EndProcedure PfSuboperate

▲Back to the top▲