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

# pfomf.htm
Structures
PFOMF_RECORD
PFOMF_THREAD_TABLE
Encodings
PfomfEnc
PfomfFixuppMethods
PfomfRecTypes
Procedures
PfomfCompile
PfomfLoadDataBlock
PfomfLoadExtern
PfomfLoadGroup
PfomfLoadModule
PfomfLoadName
PfomfLoadPgm
PfomfLoadFixData
PfomfLoadRecord
PfomfLoadSegment
PfomfStoreData
PfomfStoreFixup
PfomfStoreIndex
PfomfStoreModule
PfomfStoreName
PfomfStoreRecord

This source PF generates and loads EuroAssembler output object file in program format OMF (16bit or 32bit DOS or OS/2 object file).

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

     pfomf HEAD ; Start module interface.

↑ PFOMF_RECORD
  Files in Object Module Format (PROGRAM FORMAT=OMF or LIBOMF) consist of variable-size records with this structure.

    PfomfPayloadSize EQU 0 ; Variable netto size of carried data.
    PFOMF_RECORD STRUC
    .Type     D B ; One of PfomfRecTypes.
    .Size     D W ; Size of .Payload plus .Checksum. Brutto record size = 3 + .Size.
    .Payload  D PfomfPayloadSize * B ; Data.
    .Checksum D B ; Completes the sum of all bytes in record so that sum\256=0.
      ENDSTRUC PFOMF_RECORD

↑ PFOMF_THREAD_TABLE
  This structure describes threads used in OMF FIXUPP handling. Each member is a pointer to SSS segment or NULL.
  It is used in PfomfLoadPgm.FIXUPP

    PFOMF_THREAD_TABLE STRUC
    .Frame0  D D ; Pointer to segment referred by frame  thread 0.
    .Frame1  D D ; Pointer to segment referred by frame  thread 1.
    .Frame2  D D ; Pointer to segment referred by frame  thread 2.
    .Frame3  D D ; Pointer to segment referred by frame  thread 3.
    .Target0 D D ; Pointer to segment referred by target thread 0.
    .Target1 D D ; Pointer to segment referred by target thread 1.
    .Target2 D D ; Pointer to segment referred by target thread 2.
    .Target3 D D ; Pointer to segment referred by target thread 3.
      ENDSTRUC PFOMF_THREAD_TABLE

↑ PfomfEnc
  Encoding of binary flags used in processing of object file in OMF format.

    pfomfRec32     = 0x0000_0001 ; Odd OMF record type (32bit, instead of 16bit).
    pfomfRecLi32   = 0x0000_0002 ; Odd OMF LIDATA record type (32bit, instead of 16bit).
    pfomfLiPending = 0x0000_0004 ; Unexpanded LIDATA is pending in LiInpEmitBuf and LiInpRelocBuf.
    pfomfImport    = 0x0000_0010 ; OMF module is being imported rather than linked.
    pfomfByOrdinal = 0x0000_0020 ; Symbol is imported by ordinal rather than by name.

↑ PfomfRecTypes
  Encoding of record type names defined in OMF specification which are supported by EuroAssembler.

    THEADR   = 0x80
    LHEADR   = 0x82
    COMENT   = 0x88
    MODEND   = 0x8A
    MODEND32 = 0x8B
    EXTDEF   = 0x8C
    TYPDEF   = 0x8E
    PUBDEF   = 0x90
    PUBDEF32 = 0x91
    LINNUM   = 0x94
    LINNUM32 = 0x95
    LNAMES   = 0x96
    SEGDEF   = 0x98
    SEGDEF32 = 0x99
    GRPDEF   = 0x9A
    FIXUPP   = 0x9C
    FIXUPP32 = 0x9D
    LEDATA   = 0xA0
    LEDATA32 = 0xA1
    LIDATA   = 0xA2
    LIDATA32 = 0xA3
    LIBHDR   = 0xF0
    LIBEND   = 0xF1

↑ PfomfFixuppMethods
  Encoding of THREAD subrecord (starting with pfomfTh~) relocation methods ( Frame, Target)
  and FIXUP subrecord (starting with pfomfFx~) relocation methods ( Frame, Target)

    pfomfThF0 = 010_000_00b ; Frame is specified by SEGDEF index.
    pfomfFxF0 = 0000_0000b
    pfomfThF1 = 010_001_00b ; Frame is specified by GRPDEF index.
    pfomfFxF1 = 0001_0000b
    pfomfThF2 = 010_010_00b ; Frame is specified by EXTDEF index.
    pfomfFxF2 = 0010_0000b
    pfomfThF3 = 010_011_00b ; Frame is specified by paragraph absolute address. Not supported.
    pfomfFxF3 = 0011_0000b
    pfomfThF4 = 010_100_00b ; Frame is specified by SEGDEF of previous L?DATA record. No frame index.
    pfomfFxF4 = 0100_0000b
    pfomfThF5 = 010_101_00b ; Frame equals to target. No frame index.
    pfomfFxF5 = 0101_0000b
    pfomfThT0 = 000_000_00b ; Target is specified by SEGDEF index. Displacement follows.
    pfomfFxT0 = 00000_000b
    pfomfThT1 = 000_001_00b ; Target is specified by GRPDEF index. Displacement follows.
    pfomfFxT1 = 00000_001b
    pfomfThT2 = 000_010_00b ; Target is specified by EXTDEF index. Displacement follows.
    pfomfFxT2 = 00000_010b
    pfomfThT3 = 000_011_00b ; Target is specified by paragraph absolute address. Not supported.
    pfomfFxT3 = 00000_011b
    pfomfThT4 = 000_100_00b ; Target is specified by SEGDEF index. No displacement.
    pfomfFxT4 = 00000_100b
    pfomfThT5 = 000_101_00b ; Target is specified by GRPDEF index. No displacement.
    pfomfFxT5 = 00000_101b
    pfomfThT6 = 000_110_00b ; Target is specified by EXTDEF index. No displacement.
    pfomfFxT6 = 00000_110b

     ENDHEAD pfomf  ; End of module interface.

↑ PfomfStoreName Buffer, NamePtr, NameSize
  PfomfStoreName will store the input Name to Buffer in Pascal string format, i.e. NameSize as unsigned byte followed with that many actual characters.

  NameSize should not exceed 255 characters.

  Size of data in Buffer will not exceed 1020 bytes, obeying max.allowed OMF record size 1024. Should NameSize+BufferSize exceed 1020, the name is not written and CF is set.

Input
  Buffer is pointer to BUFFER where the name will be written to.
  NamePtr is pointer to 1st character of the name.
  NameSize is number of characters in the name.
Output
  CF=0 Name was stored to Buffer in Pascal format. If NameSize was above 254, E8513 was reported and the name truncated.
  CF=1 Name was not stored because Buffer contents would have exceeded 1020.
Error
  E8513 Segment or symbol name "!1S" exceeds 254 characters. Truncated in OMF.

    PfomfStoreName Procedure Buffer, NamePtr, NameSize
         MOV EBX,[%Buffer]
         MOV EDI,[%NamePtr]
         MOV EDX,[%NameSize]
         MOV ECX,255
         CMP EDX,ECX
         JC .10:
         LEA EAX,[%NamePtr]
         Msg '8513',EAX                              ; Segment or symbol name "!1S" exceeds 254 characters. Truncated in OMF.
         MOV EDX,ECX ; Truncate name.
     .10:BufferRetrieve EBX                          ; Get old buffer contents to ESI,ECX.
         ADD ECX,EDX
         CMP ECX,1021
         CMC
         JC .90:                                     ; Refuse if new record size too big.
         BufferStorePascalString EBX,EDI,Size=EDX
         CLC
    .90:EndProcedure PfomfStoreName

↑ PfomfStoreRecord OutputStream, RecType, RecBuffer
  PfomfStoreRecord will construct and store one OMF record to OutputStream.
  OMF record consists of 8bit record type, 16bit word with record length (specified by RecBuffer contents), the actual record contents and 8bit checksum.
Input
  OutputStreamis pointer to STREAM where the record will be written to.
  RecType contains 8bit OMF record type in LSB.
  RecBuffer is pointer to BUFFER with record contents (payload). It will be cleared on successful storage.
Output
  The complete OMF record is written to OutputStream. Input RecBuffer is then cleared.
Error
  are not expected.
Invoked by
  PflibomfCompile PfomfStoreData

    PfomfStoreRecord Procedure OutputStream, RecType, RecBuffer
         MOV EBX,[%RecType]                          ; BL is checksum accumulator, too.
         StreamStoreByte [%OutputStream],BL          ; Store Record Type.
         BufferRetrieve [%RecBuffer]
         MOV EDI,ESI
         MOV EDX,ECX
         JECXZ .20:
         CMP ECX,1020                                ; Maximal record size allowed by OMF specification.
         Msg cc=A,'9975',ECX,EBX                     ; Internal error: record size !1D exceeded 1024 in OMF record type 0x!2H.
         INC ECX                                     ; +1 the checksum byte.
         ADD BL,CL                                   ; Update checksum with Record Length lower byte.
         ADD BL,CH                                   ; Update checksum with Record Length higher byte.
         DEC ECX
     .10:LODSB
         ADD BL,AL                                   ; Update checksum with payload data.
         LOOP .10:
     .20:AND EDX,0x0000_FFFF
         INC EDX
         StreamStoreWord [%OutputStream],DX          ; Store Record Length.
         DEC EDX
         StreamStore [%OutputStream],EDI,EDX         ; Store Record payload.
         NEG BL
         StreamStoreByte [%OutputStream],BL          ; Checksum byte.
         BufferClear [%RecBuffer]
       EndProcedure PfomfStoreRecord

↑ PfomfStoreIndex Buffer, Index
  PfomfStoreRecord will store 1 or 2 byte of binary Index to output Buffer.
  If the Index is 0..127, one byte is stored.
  If the Index is 128..32767, two bytes are stored. MSB of the index incremented by 0x80 is stored first, then LSB follows.
  If the Index is above 32767=0x7FFF, that saturated value is stored and message E8519 is reported.
Input
  Buffer is pointer to BUFFER where the index will be written to.
  Index is the value of index (0..32767).
Output
  CF=0, one or two bytes are written to Buffer.
Error
  CF=1, 0xFFFF is written to Buffer.
Invoked by
  PfomfStoreData PfomfStoreFixup

    PfomfStoreIndex Procedure Buffer, Index
         SUB ECX,ECX
         MOV EDX,[%Index]
         INC ECX
         CMP EDX,127
         JNA .20:
         INC ECX
     .20:BufferNew [%Buffer],ECX                     ; Returns ptr to 1 or 2 allocated bytes in EAX.
         CMP CL,1
         JE .80:
         CMP EDX,32767
         JNA .50:
         Msg '8519'                                  ; Number of indexed references in OMF exceeded 32767. Truncated.
         MOV DX,0xFFFF
         STC
         JMP .60:
    .50: OR DH,0x80
    .60: MOV [EAX],DH
         LEA EAX,[EAX+1]
    .80: MOV [EAX],DL
       EndProcedure PfomfStoreIndex

↑ PfomfStoreData Stream, Segment
  PfomfStoreData writes OFM records of type LEDATA and FIXUPP to the output stream until all segment data and relocations are stored.

  > EuroAssembler does not create iterated LIDATA records.

Input
  Stream is pointer to STREAM where the OMF records should be written to.
  Segment is pointer to SSS object.
Output
  Segment data are stored to stream.
Error
  Errors are reported with macro Msg.
Invoked by
  PfomfStoreModule
Invokes
  EaBufferRelease EaBufferReserve PfomfStoreFixup PfomfStoreIndex PfomfStoreRecord

    PfomfStoreData Procedure Stream, Segment
    DataBuf    LocalVar ; Buffer for emitted data.
    DataPtr    LocalVar ; Pointer to unstored data in Segment.EmitBuffer. Increased after each LEDATA store.
    DataEnd    LocalVar ; Pointer to the end of data in Segment.EmitBuffer.
    OffsPtr    LocalVar ; Offset corresponding to the 1st byte of current LEDATA record. Increased after each LEDATA store.
    OffsEnd    LocalVar ; Offset behind the last byte of current LEDATA record.
    Fix16Buf   LocalVar ; Buffer for 16bit fixup subrecords.
    Fix32Buf   LocalVar ; Buffer for 32bit fixup subrecords.
    RelocPtr   LocalVar ; Pointer to unstored RELOC in Segment.RelocBuffer. Increased after each subrecord store.
    RelocEnd   LocalVar ; Pointer to  end of RELOCs in Segment.RelocBuffer.
         Invoke EaBufferReserve::,PfomfStoreData
         MOV [%DataBuf],EAX
         Invoke EaBufferReserve::,PfomfStoreData
         MOV [%Fix16Buf],EAX
         Invoke EaBufferReserve::,PfomfStoreData
         MOV [%Fix32Buf],EAX
         MOV EBX,[%Segment]
         StreamGetSize [%Stream]
         MOV [EBX+SSS.BottomFA],EAX
         BufferRetrieve [EBX+SSS.EmitBuffer]         ; Load all emitted data.
         TEST ECX
         JZ .90:                                     ; If there are no data in segment.
         MOV [%DataPtr],ESI                          ; Start with the 1st data byte.
         ADD ECX,ESI
         MOV [%DataEnd],ECX
         BufferRetrieve [EBX+SSS.RelocBuffer]        ; Load all RELOC records.
         MOV [%RelocPtr],ESI
         ADD ECX,ESI
         MOV [%RelocEnd],ECX
         MOV EDX,[EBX+SSS.BottomLow]
         MOV [%OffsPtr],EDX
         ; Keep storing LEDATA records in the loop .10:...80:, optionally followed with FIXUP record(s).
         ; The width of LEDATA record (0xA0 or 0xA1) depends on segment width.
     .10:Invoke PfomfStoreIndex,[%DataBuf],[EBX+SSS.SegmIndex] ; LEDATA segment index.
         JSt [EBX+SSS.Status],sssWidth16,.15:
         BufferStoreDword [%DataBuf],EDX             ; LEDATA32 offset.
         JMP .20:
     .15:BufferStoreWord [%DataBuf],EDX              ; LEDATA offset.
     .20:MOV EAX,[%DataEnd]                          ; Compute maximal OffsEnd due to the limited OMF record size (1024).
         SUB EAX,[%DataPtr]
         CMP EAX,1014
         JNA .25:
         MOV EAX,1014
     .25:ADD EAX,[%OffsPtr]
         MOV [%OffsEnd],EAX
         ; Data between OffsPtr..OffsEnd are prepared for LEDATA.
         ; OffsEnd may be decreased later if some relocation spans over it or if correspoding fixup subrecord overflows FIXUP record size.
     .30:MOV ESI,[%RelocPtr]
         CMP ESI,[%RelocEnd]
         JNB .60:                                    ; If there are no more relocations for this segment.
         MOV EAX,[ESI+RELOC.OrgLow]
         CMP EAX,[%OffsPtr]
         JB .40:                                     ; If RELOC doesn't belong to the current LEDATA chunk.
         MOV ECX,relocWidthMask
         AND ECX,[ESI+RELOC.Status]
         SAR ECX,19                                  ; Convert relocWidth (16,32,64) to 2,4,8 bytes.
         ADD ECX,EAX                                 ; End-of-location offset of relocation ESI.
         CMP ECX,[%OffsEnd]
         JA .50:                                     ; If relocated target does not fit to data between OffsPtr..OffsEnd.
         ; Relocation ESI is relevant to LEDATA chunk, it will be stored.
         MOV EAX,[%Fix16Buf]                         ; Decide what width (16 or 32) FIXUPP should be used.
         JSt [ESI+RELOC.Status],relocWidth16,.35:
         MOV EAX,[%Fix32Buf]
     .35:Invoke PfomfStoreFixup,EAX,ESI,[%OffsPtr],EBX ; It is our fixup.
         JC .50:                                     ; If fixup couldn't be stored because its  OMF record is full.
     .40:ADD ESI,SIZE#RELOC                          ; Try the next relocation if fixup subrecord was successfully stored.
         MOV [%RelocPtr],ESI
         JMP .30:                                    ; Check if the next RELOC corresponds with LEDATA, too.
     .50:; Relocation ESI could not be stored because FixBuf is full or because the relocated location spans over OffsEnd.
         MOV EAX,[ESI+RELOC.OrgLow]                  ; The whole location will be omitted from LEDATA.
         CMP EAX,[%OffsEnd]
         JAE .60:                                    ; If already omitted.
         MOV [%OffsEnd],EAX
     .60:; OffsEnd is finalized, now it's time to flush LEDATA and FIXUP records.
         MOV EDX,[%OffsEnd]
         SUB EDX,[%OffsPtr]                          ; EDX is now LEDATA data netto size.
         JNA .90:                                    ; If there are no more data in segment.
         BufferStore [%DataBuf],[%DataPtr],EDX
         ADD [%DataPtr],EDX                          ; Prepare pointers for the next LEDATA record.
         ADD [%OffsPtr],EDX
         MOV AL,LEDATA                               ; Record type A0 or A1.
         JSt [EBX+SSS.Status],sssWidth16,.65:
         INC EAX                                     ; LEDATA32 = 0xA1.
     .65:Invoke PfomfStoreRecord,[%Stream],EAX,[%DataBuf] ; Store LEDATA.
         BufferRetrieve [%Fix16Buf]
         JECXZ .70:                                  ; Skip if there are no 16bit fixups for this LEDATA chunk.
         Invoke PfomfStoreRecord,[%Stream],FIXUPP,[%Fix16Buf] ; Store FIXUPP16.
     .70:BufferRetrieve [%Fix32Buf]
         JECXZ .80:                                  ; Skip if there are no 32bit fixups for this LEDATA chunk.
         Invoke PfomfStoreRecord,[%Stream],FIXUPP32,[%Fix32Buf] ; Store FIXUPP32.
     .80:JMP .10:                                    ; Go and store the next LEDATA/FIXUP pair.
     .90:Invoke EaBufferRelease::,[%Fix32Buf]
         Invoke EaBufferRelease::,[%Fix16Buf]
         Invoke EaBufferRelease::,[%DataBuf]
       EndProcedure PfomfStoreData

↑ PfomfStoreFixup FixBuffer, Reloc, Offset, SegmPtr
  PfomfStoreFixup will store one FIXUP subrecord to the FixBuffer.
  `relocWidth` flag in `Reloc.Status` specifies if the currently stored OMF FIXUPP record is 16bit or 32bit.

  > THREAD subrecords are not generated by EuroAssembler.

Input
  FixBuffer is pointer to the output BUFFER where the subrecord should be written to.
  Reloc is pointer to an input RELOC object which will be transformed to a fixup subrecord.
  Offset is offset (0..1021) of data payload in previous LEDATA record from the start of segment.
  SegmPtr is pointer to the segment SSS where the relocated object is.
Output
  CF=0, fixup subrecord was stored to FixBuffer.
Error
  CF=1 if the fixup could not be stored because FixBuffer size would exceed 1021 bytes. Caller should flush FixBuffer and then try again.
Invokes
  PfomfStoreIndex
Invoked by
  PfomfStoreData

    PfomfStoreFixup Procedure FixBuffer, Reloc, Offset, SegmPtr
        BufferRetrieve [%FixBuffer]                  ; Examine buffer size occupied by previous subrecords.
        CMP ECX,1021-11                              ; Size of the fixup subrecord in the worst case may be 11 bytes.
        CMC
        JC .90:                                      ; Refuse this subrecord if FixBuffer is full.
        MOV ESI,[%Reloc]
        MOV EDX,[ESI+RELOC.OrgLow]
        SUB EDX,[%Offset]
        TEST EDX,~0x3FF                              ; EDX is now the address 0..1023 relative to the payload in previous LEDATA.
        JNZ .F9975:                                  ; Internal error: record size !1D exceeded 1024 in OMF record type 0x!2H.
        ; Construct 16bit FIXUP subrecord field Locat in DX.
        XCHG DH,DL                                   ; Locat is big endian.
        MOV EAX,[ESI+RELOC.Status]
        JSt EAX,relocIgnore,.90:
        AND EAX,relocTypeMask                        ; Dispatch relocation type Far,Para,Rel,Abs.
        Dispatch EAX,relocAbsVA,relocRel,relocPara,relocFar
    .F9974:Msg '9974',[ESI+RELOC.Status],[%SegmPtr],[ESI+RELOC.OrgLow] ; Internal error: unhandled relocation type 0x!1W at [!2S]:!3Hh.',0
    .Err:JMP .90:
    .W3831:Msg '3831',[%SegmPtr],[ESI+RELOC.OrgLow]  ; Self-relative relocation to an absolute VA at [!1S]:!2Hh is not linkable.
        JMP .Err:
    .W3832:Msg '3832',[%SegmPtr],[ESI+RELOC.OrgLow]  ; Far relocation to an absolute VA at [!1S]:!2Hh is not linkable.
        JMP .Err:
    .F9975:Msg '9975',EDX,0x9C                       ; Internal error: record size !1D exceeded 1024 in OMF record type 0x!2H
        JMP .Err:

    .relocAbsVA:JSt [ESI+RELOC.Status],relocWidth32,.05:
        OR DL,11_0001_00b                            ; Segment-relative, Location=1 (16bit offset).
        JMPS .30:
    .05:OR DL,11_1001_00b                            ; Segment-relative, Location=9 (32bit offset).
        JMPS .30:

    .relocFar:JSt  [ESI+RELOC.Status],relocWidth32,.10:
        OR DL,11_0011_00b                            ; Segment-relative, Location=3 (segment:16bit offset).
        JMPS .30:
    .10:OR DL,11_1011_00b                            ; Segment-relative, Location=11 (segment:32bit offset).
        JMPS .30:

    .relocPara:
        OR DL,11_0010_00b                            ; Segment-relative, Location=2 (base segment).
        JMPS .30:

    .relocRel:MOV EAX,2                              ; IP-relative 16bit relocation OMF correction.
        JSt  [ESI+RELOC.Status],relocWidth32,.20:
        OR DL,10_0001_00b                            ; IP-relative, Location=1 (16bit offset).
        JMPS .25:
    .20:OR DL,10_1001_00b                            ; IP-relative, Location=9 (32bit offset).
        ADD EAX,2                                    ; IP-relative 32bit relocation OMF correction.
    .25:ADD [ESI+RELOC.AddendLow],EAX
        ADC [ESI+RELOC.AddendHigh],0
    .30:BufferStoreWord [%FixBuffer],EDX             ; Store field Locat.

        ; Construct 8bit FIXUP subrecord field FixData in DL.
        MOV ECX,[ESI+RELOC.Symbol]
        JECXZ .40:
        MOV EDI,[ECX+SYM.Section]                    ; Target SSS object (segment|group|extern).
        TEST EDI
        JNZ .45:                                     ; Skip when target symbol ECX is not scalar.
    .40:JSt [ESI+RELOC.Status],relocRel,.W3831:      ; Self-relative relocation to an absolute VA at [!1S]:!2Hh is not linkable.
        JSt [ESI+RELOC.Status],relocFar,.W3832:      ; Far relocation to an absolute VA at [!1S]:!2Hh is not linkable.
        JMP .F9974:                                  ; Internal error: unhandled relocation type 0x!1W at [!2S]:!3Hh.
    .45:MOV ECX,[EDI+SSS.SegmPtr]
        JECXZ .47:
        MOV EDI,ECX
    .47:;;;MOV ECX,[EDI+SSS.GroupPtr]
        ;;;JECXZ .48:
        ;;;MOV EDI,ECX
    .48:MOV EAX,[EDI+SSS.Status]                     ; EAX is now the type of target SSS object.
        MOV ECX,[ESI+RELOC.AddendLow]                ; ECX is additional displacement of relocated object (usually 0).
        JSt EAX,sssGroup,.sssGroup:
        JSt EAX,sssSegment,.sssSegment:
        JSt EAX,sssExtern,.sssExtern:
        JMP .F9974:                                  ; Internal error: unhandled relocation type 0x!1W at [!2S]:!3Hh.

    .StoreFixDat: BufferStoreByte [%FixBuffer],EDX
                  RET
    .sssExtern:
        MOV DL,0101_0110b                            ; Frame is determined by Target EXTDEF.  F5,T6,no displacement.
        JECXZ .50:                                   ; Skip when no additional displacement is present.
        AND DL,1111_1011b                            ; Frame is determined by Target EXTDEF.  F5,T2,displacement present.
    .50:CALL .StoreFixDat:
        Invoke PfomfStoreIndex,[%FixBuffer],[EDI+SSS.SegmIndex] ; TargetDatum EXTDEF or GRPDEF.
        JMP .StoreTargetDisplacement:

    .sssGroup:
        MOV DL,0101_0101b                            ; Frame is determined by Target GRPDEF. F5,T5,no displacement.
        JECXZ .50:
        AND DL,1111_1011b                            ; Frame is determined by Target GRPDEF. F5,T1,displacement present.
        JMP .50:

    .sssSegment:
        ; Group membership is ignored in fixup.  Target segment EDI does not belong to any group.
        MOV DL,0101_0100b                            ; Frame is determined by Target SEGDEF. F5,T4,no displacement.
        JECXZ .75:
        AND DL,1111_1011b                            ; Frame is determined by Target SEGDEF. F5,T0,displacement present.
    .75:CALL .StoreFixDat:
    .80:Invoke PfomfStoreIndex,[%FixBuffer],[EDI+SSS.SegmIndex] ; TargetDatum SEGDEF.
    .StoreTargetDisplacement:                        ; Store the displacement ECX as a word or dword, depending on P-bit in DL.
        TEST DL,0000_0100b                           ; P-bit in FixDat field.
        JNZ .90:                                     ; Skip if P=1, no target displacement.
        JSt [ESI+RELOC.Status],relocWidth16,.85:
        BufferStoreDword [%FixBuffer],ECX
        JMP .90:
    .85:BufferStoreWord [%FixBuffer],ECX
    .90:EndProcedure PfomfStoreFixup

↑ PfomfCompile OutputStream, Pgm
  PfomfCompile is constructor of output file image in linkable format OMF.
Input
  OutputStream is pointer to an empty STREAM for the output image contents.
  Pgm is pointer to PGM representing the completely assembled program, optionally combined with other linked modules.
Output
  OutputStream is filled with output file contents.
Error
  Errors are reported with macro Msg.
Invoked from
  PfOutput
Invokes
  PfomfStoreModule
Tested by
  t7921 t7922 t7923 t7924 t7925 t7926 t8350

    PfomfCompile Procedure OutputStream, Pgm
         Invoke PfomfStoreModule, [%OutputStream],[%Pgm]
        EndProcedure PfomfCompile

↑ PfomfStoreModule OutputStream, Pgm
  PfomfStoreModule is constructor of one OMF module (program), which beings with THEADR or LHEADR and which ends with MODEND or MODEND32 record.
  This procedure is used to compile output files in format OMF and LIBOMF.
Input
  OutputStream is pointer to STREAM for the output module contents.
  Pgm is pointer to PGM representing the base or library module program.
Output
  OMF module records are written to OutputStream.
Error
  Errors are reported with macro Msg.
Invokes
  EaBufferRelease EaBufferReserve PfomfStoreData PgmOrderSegments PgmResizeGroups
Invoked by
  PflibomfStoreModule PfomfCompile

    PfomfStoreModule Procedure OutputStream, Pgm
    RecBuf     LocalVar ; Buffer for the netto contents of one OMF record (without record type, length, checksum).
    NameIndex  LocalVar ; Ordinal number of segment name in LNAMES (1,2,3,,,).
    SegIndex   LocalVar ; Ordinal number of segment in GRPDEF (1,2,3,,,).  Kept in SSS.SegmIndex.
    GrpIndex   LocalVar ; Ordinal number of  group  in PUBDEF (1,2,3,,,).  Kept in SSS.SegmIndex.
    ExtIndex   LocalVar ; Ordinal number of  extern in PUBDEF (1,2,3,,,).  Kept in SSS.SegmIndex.
         MOV EDX,[%Pgm]
         ; Initialize temporary data structures.
         Invoke EaBufferReserve::,PfomfStoreModule
         MOV [%RecBuf],EAX
         Invoke PgmOrderSegments::,EDX
         ; Create OMF records.
         MOV EAX,LHEADR      ; Assume library member.
         JSt [EDX+PGM.Status],pgmIsModule,.10:
         MOV EAX,THEADR      ; Assume OMF module.
    .10: CALL .THEADR:       ; Module name.
         CALL .COMENT_00:    ; Compiler info.
         ; If message "I0010 EuroAssembler version !1S started." is suppressed,
         ;   do not store the version info in COMENT_00 and skip the dependency info in COMENT_E9.
         ;   This is used in €ASM tests where the produced object file should not depend on €ASM version.
         TESTB [Ea.Eaopt.NoWarn:: + 0010/8],0x01<<(0010\8)         ; >> Test if NOWARN=0010.
         JNZ .20:            ; Skip when NOWARN=0010.
         CALL .COMENT_E9:    ; File dependency.
     .20:CALL .COMENT_9D:    ; CPU and model info.
         CALL .COMENT_A0_01: ; Imported symbols.
         CALL .COMENT_A0_02: ; Exported symbols.
         CALL .LNAMES:       ; Name declarations.
         CALL .SEGDEF:       ; Segment definitions.
         CALL .EXTDEF:       ; Extern definitions.
         CALL .GRPDEF:       ; Group definitions.
         CALL .PUBDEF16:     ; Public definitions.
         CALL .PUBDEF32:     ; Public with offset above 64KB.
         ; Emitted contents and relocations are handled  with records
         ;   LEDATA and FIXUPP called via PfomfStoreData.
         MOV EDX,[%Pgm]
         BufferRetrieve [EDX+PGM.SegOrdBuffer]
         SHR ECX,2
         JZ .70:
     .30:LODSD
         JNSt [EAX+SSS.Status],sssSegment,.50:
         JSt [EAX+SSS.Purpose],sssPurposeBSS,.50:                 ; Do not emit.
         JSt [EAX+SSS.Purpose],sssPurposeCODE|sssPurposeDATA|sssPurposeRODATA,.40: ; Always emit.
         JNSt [EAX+SSS.Status],sssNotBSS,.50:                     ; Emit stack segment only if it contains initialized data.
     .40:Invoke PfomfStoreData,[%OutputStream],EAX                ; Store OMF records LEDATA and FIXUPP of segment EAX.
     .50:LOOP .30:                                                ; The next segment.
     .70:CALL .MODEND:                                            ; End of OMF file.
     .90:Invoke EaBufferRelease::,[%RecBuf]
         Invoke PgmResizeGroups::,[%Pgm]
        EndProcedure PfomfStoreModule

    ; OMF records handlers write %Pgm information to %RecBuf and then store one OMF record
    ;    into %OutputStream, using the procedure PfomfStoreRecord.

    PfomfStoreModule.THEADR: PROC ; Record of type EAX (THEADR or LHEADR) with module name.
         MOV ESI,[%Pgm]
         BufferStorePascalString [%RecBuf],[ESI+PGM.NamePtr],Size=[ESI+PGM.NameSize] ; Pgm name.
         Invoke PfomfStoreRecord,[%OutputStream],EAX,[%RecBuf]
         RET
         ENDP PfomfStoreModule.THEADR:

    PfomfStoreModule.COMENT_00: PROC  ; Record with compiler info, e.g. "EuroAssembler 20161016 Win"
         BufferStoreWord [%RecBuf],0                   ; 0x88 comment class 00, purge, list.
         BufferStore [%RecBuf],=B"EuroAssembler",13
         ; If the message "I0010 EuroAssembler version !1S started." is suppressed, skip further info in record.
         TESTB [Ea.Eaopt.NoWarn:: +0010/8],0x01<<(0010\8) ; >> Test if NOWARN=0010.
         JNZ .c1:
         BufferStoreByte [%RecBuf],' '                 ; Otherwise append €ASM version and OS info.
         BufferStore [%RecBuf],Ea.Version::,8
         BufferStoreByte [%RecBuf],' '
         MOV ESI,Ea.EuroasmOS::
         GetLength$ ESI
         BufferStore [%RecBuf],ESI,ECX
     .c1:Invoke PfomfStoreRecord,[%OutputStream],COMENT,[%RecBuf]
         RET
         ENDP PfomfStoreModule.COMENT_00:

    PfomfStoreModule.COMENT_9D: PROC ; Record with CPU and MODEL info (Xenix).
         MOV EDX,[%Pgm]
         BufferStoreWord [%RecBuf],0x9D00                  ; Comment class 9D, purge, list.
         MOV EDI,[Ea.Eaopt.Machine::]
         MOV AL,'0'
         AND EDI,iiCPU_CpuMask
         JZ .c2:                                           ; If iiCPU_086, leave AL='0'.
         BSR ECX,EDI                                       ; ECX is now 6..12 for CPU=186..X64.
         SUB CL,5
         ADD AL,CL                                         ; AH is now '0'..'7' for CPU=086..X64.
     .c2:MOV EDI,[EDX+PGM.Pgmopt.Status]
         AND EDI,pgmoptModelMask
         Invoke DictSearchByData::,DictProgramModels::,EDI ; Returns ESI=ptr to "SMALL","COMPACT" etc.
         MOV AH,'?'
         JC .c5:                                           ; Skip if PROGRAM MODEL= is not specified.
         MOV ESI,[ESI]                                     ; Let ESI point to the model name.
         MOV AH,[ESI]                                      ; Load the first letter of model specification.
         OR AH,'s'^'S'                                     ; Convert to lowercase.
     .c5:XCHG EAX,EDI                                      ; Temporary save EAX to EDI.
         BufferNew [%RecBuf],2
         XCHG EDI,EAX
         STOSW                                             ; CPU and model info.
         Invoke PfomfStoreRecord,[%OutputStream],COMENT,[%RecBuf]
         RET
         ENDP PfomfStoreModule.COMENT_9D:

    PfomfStoreModule.COMENT_E9: PROC ; Record(s) with dependency files info (Borland).
         BufferStoreWord [%RecBuf],0xE940                  ; 0x88 comment class 0xE9, purge, nolist.
         ; First store records of the main source file.
         BufferStore [%RecBuf],Ea.SrcTime::,4              ; DosDateTime of source file.
         LEA EAX,[Ea.SrcFile.Name::]
         BufferStorePascalString [%RecBuf],EAX             ; Pascal-formated string with ASCIIZ filename.
         Msg cc=C,'8512',EAX                               ; Length of linked file name "!1$" exceeds 254 characters.
         Invoke PfomfStoreRecord,[%OutputStream],0x88,[%RecBuf]
         ; Now store records of all included files. %RecBuf was cleared.
         SUB EDI,EDI                                       ; Index in PGM.Incl*Tables.
         MOV EDX,[%Pgm]
     .c2:BufferStoreWord [%RecBuf],0xE940                  ; 0x88 comment class 0xE9, purge, nolist.
         CMP EDI,[EDX+PGM.InclFilesNr]                     ; How many files are included in this program source.
         JAE .c8:                                          ; If no more files.
         MOV ESI,[EDX+PGM.InclFileTimeTable]               ; ESI=^array of dwords with DosDateTime.
         LEA ESI,[ESI+4*EDI]
         BufferStore [%RecBuf],ESI,4                       ; DosDateTime of included file.
         MOV ESI,[EDX+PGM.InclFilesTable]                  ; ESI=^array of pointers to FILE structure.
         MOV EAX,[ESI+4*EDI]                               ; EAX=^FILE.
         LEA EAX,[EAX+FILE.Name]
         BufferStorePascalString [%RecBuf],EAX             ; Pascal-formated string with filename.
         Msg cc=C,'8512',EAX                               ; Length of linked file name "!1$" exceeds 254 characters.
         Invoke PfomfStoreRecord,[%OutputStream],COMENT,[%RecBuf]
         INC EDI
         JMP .c2:                                          ; The next included file name.
     .c8:Invoke PfomfStoreRecord,[%OutputStream],COMENT,[%RecBuf] ; The last empty COMENT 0xE9 signalizes end of dependencies.
         RET
         ENDP PfomfStoreModule.COMENT_E9:

    PfomfStoreModule.COMENT_A0_01: PROC ; Import definitions, encoded as COMENT class A0 type 01.
         MOV EDX,[%Pgm]
         MOV ECX,0xFF00_0000                               ; ImportedByOrd marker.
         ListGetFirst [EDX+PGM.SymList]                    ; Enumerate program symbols.
         JZ .i9:
     .i1:MOV EBX,EAX
         JNSt [EBX+SYM.Status],symImport,.i8:
         MOV EAX,[EBX+SYM.DllNameSize]
         TEST EAX
         JNZ .i2:
         MOV [EBX+SYM.DllNamePtr],=B"%EuroasmDefaultDllName"
         MOV [EBX+SYM.DllNameSize],%EuroasmDefaultDllNameSize
     .i2:MOV EAX,0x0001A080                                ; Comment class A0,nopurge,list,subtype 01,ImportedByName.
         JNSt [EBX+SYM.Status],symImportedByOrd,.i3:
         OR EAX,ECX                                        ; Change the ByOrdinal flag from 00 to FF.
     .i3:BufferStoreDword [%RecBuf],EAX
         MOV EDI,[EBX+SYM.InterNamePtr]
         MOV EDX,[EBX+SYM.InterNameSize]
         TEST EDX
         JNZ .i4:
         MOV EDI,[EBX+SYM.NamePtr]
         MOV EDX,[EBX+SYM.NameSize]
     .i4:Invoke PfomfStoreName,[%RecBuf],EDI,EDX
         Invoke PfomfStoreName,[%RecBuf],[EBX+SYM.DllNamePtr],[EBX+SYM.DllNameSize]
         TEST EAX,ECX
         JNZ .i6:                                          ; If ImportedByOrd.
         Invoke PfomfStoreName,[%RecBuf],[EBX+SYM.NamePtr],[EBX+SYM.NameSize] ; ImportedByName.
         JMP .i7:
     .i6:MOV EAX,[EBX+SYM.OrdinalNr]
         BufferStoreWord [%RecBuf],EAX,                    ; Store WORD ordinal/name.
     .i7:Invoke PfomfStoreRecord,[%OutputStream],COMENT,[%RecBuf]
     .i8:ListGetNext EBX                                   ; The next symbol.
         JNZ .i1:
     .i9:RET
         ENDP PfomfStoreModule.COMENT_A0_01:

    PfomfStoreModule.COMENT_A0_02: PROC ; Export definitions, encoded as COMENT class A0 type 02.
         MOV EDX,[%Pgm]
         ListGetFirst [EDX+PGM.SymList]                    ; Enumerate program symbols.
         JZ .x9:
     .x1:MOV EBX,EAX
         JNSt [EBX+SYM.Status],symExport,.x8:
         MOV EAX,0x0002A080                                ; Comment class A0,nopurge,list,subtype 02,ExportedByName.
         ; €ASM does not support export by ordinal.
         BufferStoreDword [%RecBuf],EAX
         Invoke PfomfStoreName,[%RecBuf],[EBX+SYM.NamePtr],[EBX+SYM.NameSize] ; Exported name.
         MOV ESI,[EBX+SYM.InterNamePtr]
         MOV ECX,[EBX+SYM.InterNameSize]
         JECXZ .x5:
         Compare [EBX+SYM.NamePtr],[EBX+SYM.NameSize],ESI,ECX
         JE .x5:
         ; Exported by a different name.
         Invoke PfomfStoreName,[%RecBuf],ESI,ECX
         JMP .x7:
     .x5:; Internal name equals to exported name. Store as NULL Pascal string.
         BufferStoreByte [%RecBuf],0
     .x7:Invoke PfomfStoreRecord,[%OutputStream],COMENT,[%RecBuf]
     .x8:ListGetNext EBX                                   ; The next symbol.
         JNZ .x1:
     .x9:RET
         ENDP PfomfStoreModule.COMENT_A0_02:

    PfomfStoreModule.COMENT_A1: PROC ; Marker New OMF extension (CodeView).
         BufferStoreWord [%RecBuf],0xA140                  ; Comment class 0xA1, purge, nolist.
         Invoke PfomfStoreRecord,[%OutputStream],COMENT,[%RecBuf]
         RET
         ENDP PfomfStoreModule.COMENT_A1:

    PfomfStoreModule.LNAMES: PROC ; Names of segments and their classes will be stored to LNAMES records.
          ; Segment name index is written to SSS.SegmIndex. Segment class index is 1 if empty, otherwise SSS.SegmIndex+1.
          Invoke PfomfStoreName,[%RecBuf],0,0              ; The first stored name (%NameIndex=1) is always empty.
          MOVD [%NameIndex],1
          MOV EDX,[%Pgm]
          BufferRetrieve [EDX+PGM.SegOrdBuffer]
          SHR ECX,2
          JZ .ln9:
     .ln2:LODSD
          MOV EBX,EAX
          JNSt [EBX+SSS.Status],sssSegment|sssGroup,.ln8:
          MOV EDI,[EBX+SSS.NamePtr]
          MOV EDX,[EBX+SSS.NameSize]
          Invoke PfomfStoreName,[%RecBuf],EDI,EDX
          JNC .ln4:
          ; %NameBuf is full, we need to flush it to LNAMES record and clear it.
          Invoke PfomfStoreRecord,[%OutputStream],0x96,[%RecBuf]
          Invoke PfomfStoreName,[%RecBuf],EDI,EDX          ; This time it must succeed.
     .ln4:MOV EAX,[%NameIndex]
          INC EAX
          MOV [EBX+SSS.NameIndex],EAX
          MOV [%NameIndex],EAX
          MOV EDX,[EBX+SSS.ClassSize]
          MOV EDI,[EBX+SSS.ClassPtr]
          TEST EDX
          JZ .ln8:
     .ln5:Invoke PfomfStoreName,[%RecBuf],EDI,EDX
          JNC .ln7:
          ; %NameBuf is full, we need to flush it to LNAMES record.
          Invoke PfomfStoreRecord,[%OutputStream],0x96,[%RecBuf]
          Invoke PfomfStoreName,[%RecBuf],EDI,EDX          ; This time it must succeed.
     .ln7:INCD [%NameIndex]                                ; NameIndex of CLASS= attribute is always segment.NameIndex+1.
     .ln8:DEC ECX
          JNZ .ln2:                                        ; The next segment|group.
     .ln9:Invoke PfomfStoreRecord,[%OutputStream],LNAMES,[%RecBuf] ; Flush %NameBuf to LNAMES record.
          RET
         ENDP PfomfStoreModule.LNAMES:

    PfomfStoreModule.SEGDEF:PROC ; Segment definitions will be stored to SEGDEF records.
          MOVD [%SegIndex],0
          MOV EDX,[%Pgm]
          BufferRetrieve [EDX+PGM.SegOrdBuffer]
          SHR ECX,2
          JZ .s99:
     .s10:LODSD
          PUSH ECX
          MOV EBX,EAX
          MOV EAX,[EBX+SSS.Status]
          JNSt EAX,sssSegment,.s90:
          ; Segment ordinal in GRPDEF.
          MOV ECX,[%SegIndex]
          INC ECX
          MOV [EBX+SSS.SegmIndex],ECX
          MOV [%SegIndex],ECX
          ; Put segment attribute A (alignment) to CL bits 5..7.
          MOV EAX,[EBX+SSS.Alignment]                      ;   0,1,2,4,8,16,,,
          MOV CL,1<<5                                      ;>> A=1 (BYTE).
          BSR EDX,EAX                                      ;   EDX is now 0..9 (alignment 1..256).
          JZ .s20:                                         ;   If no alignment (BYTE).
          CMP DL,1                                         ;   ALIGN=WORD?
          JB .s20:
          MOV CL,2<<5                                      ;>> A=2 (WORD).
          JE .s20:
          MOV CL,5<<5                                      ;>> A=5 (DWORD).
          CMP DL,2                                         ;   ALIGN=DWORD?
          JE .s20:
          MOV CL,3<<5                                      ;>> A=3 (OWORD).
          CMP DL,5                                         ;   ALIGN=32?
          JB .s20:
          MOV CL,4<<5                                      ;>> A=4 (256).
     .s20:; Put segment attribute C (combine) to DL bits 2..4.
          MOV EAX,[EBX+SSS.Status]
          JSt EAX,sssPrivate,.s30:
          MOV DL,5<<2                                      ;>> C=5 (STACK).
          JSt EAX,sssStack,.s25:
          JSt [EBX+SSS.Purpose],sssPurposeSTACK,.s25:      ; If PURPOSE=STACK, force COMBINE=STACK in this format.
          MOV DL,6<<2                                      ;>> C=6 (COMMON).
          JSt EAX,sssCommon,.s25:
          MOV DL,2<<2                                      ;>> C=2 (PUBLIC).
     .s25:OR CL,DL
     .s30:JSt EAX,sssWidth16,.s40:
          ; 32bit segment.
          OR CL,0x01                                       ; Set ACBP segment attribute P (use32).
          MOV EAX,[EBX+SSS.TopLow]
          MOV EDX,[EBX+SSS.TopHigh]
          SUB EAX,[EBX+SSS.BottomLow]
          SBB EDX,[EBX+SSS.BottomHigh]
          TEST EDX
          JZ .s35:
          Msg '8524',EBX                                   ; OMF 32bit segment [!1S] size exceeded 4GB. Truncated.
          OR CL,0x02                                       ; Set ACBP segment attribute B.
          SUB EAX,EAX
     .s35:PUSH EAX                                         ; Save segment size EAX.
            BufferNew [%RecBuf],1+4                        ; ACBP+dword segment size.
            MOV EDI,EAX
            MOV EAX,ECX
            STOSB                                          ; Store ACBP attribute.
          POP EAX
          STOSD                                            ; Store 32bit segment length.
          JMP .s70:
     .s40:; 16bit segment.
          MOV EAX,[EBX+SSS.TopLow]
          MOV EDX,[EBX+SSS.TopHigh]
          SUB EAX,[EBX+SSS.BottomLow]
          SBB EDX,[EBX+SSS.BottomHigh]
          TEST EDX
          JNZ .s45:
          CMP EAX,64k
          JE .s50:
          JB .s60:
     .s45:Msg '8523',EBX,EAX                               ; OMF 16bit segment [!1S] size !2K exceeded 64KB. Truncated.
     .s50:OR CL,0x02                                       ; Set ACBP segment attribute B.
          SUB EAX,EAX
     .s60:PUSH EAX
            BufferNew [%RecBuf],1+2                        ; ACBP+word segment size.
            MOV EDI,EAX
            MOV EAX,ECX
            STOSB                                          ; Store ACBP attribute.
          POP EAX
          STOSW                                            ; Store 16bit segment length.
     .s70:; Common continuation for 16bit and 32bit segments.
          MOV EDX,[EBX+SSS.NameIndex]
          Invoke PfomfStoreIndex,[%RecBuf],EDX
          MOV ECX,[EBX+SSS.ClassSize]
          MOV EAX,1
          JECXZ .s80:                                      ; If segment class is empty, store ClassIndex=1,
          ADD EAX,EDX                                      ;  otherwise store NameIndex+1.
     .s80:Invoke PfomfStoreIndex,[%RecBuf],EAX
          Invoke PfomfStoreIndex,[%RecBuf],0               ; OverlayIndex is not used.
          MOV AL,SEGDEF
          JSt [EBX+SSS.Status],sssWidth16,.s85:
          MOV AL,SEGDEF32
     .s85:Invoke PfomfStoreRecord,[%OutputStream],EAX,[%RecBuf]
     .s90:POP ECX
          DEC ECX
          JNZ .s10:                                        ; The next segment.
     .s99:RET
         ENDP PfomfStoreModule.SEGDEF:

    PfomfStoreModule.GRPDEF: PROC ; Segment group definitions are stored to GRPDEF record(s).
          MOVD [%GrpIndex],0
          MOV EDX,[%Pgm]
          BufferRetrieve [EDX+PGM.SegOrdBuffer]
          SHR ECX,2
          JZ .g90:
    .g10: LODSD
          PUSH ECX
          MOV EBX,EAX
          JNSt [EBX+SSS.Status],sssGroup,.g80:
          MOV ECX,[%GrpIndex]                              ; Create GRPDEF record for the group EBX.
          INC ECX
          MOV [%GrpIndex],ECX
          MOV [EBX+SSS.SegmIndex],ECX
          Invoke PfomfStoreIndex,[%RecBuf],[EBX+SSS.NameIndex]
          MOV EDX,[%Pgm]
          ListGetFirst [EDX+PGM.SssList]                   ; Find segments of the group EBX.
          JZ .g60:
     .g20:MOV EDX,EAX
          JNSt [EDX+SSS.Status],sssSegment,.g50:
          CMP [EDX+SSS.GroupPtr],EBX
          JNE .g50:                                        ; Skip if this segment does not belong to group EBX.
          BufferStoreByte [%RecBuf],0xFF                   ; Grouptype field, always 0xFF.
          Invoke PfomfStoreIndex,[%RecBuf],[EDX+SSS.SegmIndex]
     .g50:ListGetNext EDX
          JNZ .g20:
     .g60:Invoke PfomfStoreRecord,[%OutputStream],GRPDEF,[%RecBuf]
     .g80:POP ECX
          DEC ECX
          JNZ .g10:
     .g90:RET
         ENDP PfomfStoreModule.GRPDEF:

    PfomfStoreModule.EXTDEF:PROC ; Extern symbols definitions.
         MOVD [%ExtIndex],0
         MOV EDX,[%Pgm]
         ListGetFirst [EDX+PGM.SymList]                    ; Enumerate program symbols.
         JZ .e9:
     .e1:MOV EBX,EAX
         JNSt [EBX+SYM.Status],symExtern|symImport,.e8:
         JSt [EBX+SYM.Status],symResolved,.e8:
         MOV ESI,[EBX+SYM.Section]
         MOV ECX,[%ExtIndex]                               ; Create EXTDEF record for the external pseudosegment ESI.
         INC ECX
         MOV [%ExtIndex],ECX
         MOV [ESI+SSS.SegmIndex],ECX
         MOV EDI,[EBX+SYM.InterNamePtr]
         MOV EDX,[EBX+SYM.InterNameSize]
         TEST EDX
         JNZ .e5:
         MOV EDI,[EBX+SYM.NamePtr]
         MOV EDX,[EBX+SYM.NameSize]
         MOV [EBX+SYM.InterNamePtr],EDI
         MOV [EBX+SYM.InterNameSize],EDX
     .e5:MOV [ESI+SSS.NamePtr],EDI
         MOV [ESI+SSS.NameSize],EDX
         Invoke PfomfStoreName,[%RecBuf],EDI,EDX           ; Pascal-string name of external symbol.
         Invoke PfomfStoreIndex,[%RecBuf],0                ; Symbol CodeView datatype is not used.
         Invoke PfomfStoreRecord,[%OutputStream],EXTDEF,[%RecBuf] ; Flush extern symbols from %RecBuf.
     .e8:ListGetNext EBX
         JNZ .e1:                                          ; The next symbol.
     .e9:RET
         ENDP PfomfStoreModule.EXTDEF:

    PfomfStoreModule.PUBDEF16:PROC ; Public 16bit symbols definitions.
         MOV EDX,[%Pgm]
         ListGetFirst [EDX+PGM.SymList]                    ; Enumerate program symbols.
         JZ .p9:
     .p1:MOV EBX,EAX
         JNSt [EBX+SYM.Status],symPublic|symExport,.p8:
         MOV EDX,[EBX+SYM.OffsetHigh]
         MOV EAX,[EBX+SYM.OffsetLow]
         TEST EDX
         JNZ .p8:                                          ; Skip 64bit symbol.
         TEST EAX,0xFFFF_0000
         JNZ .p8:                                          ; Skip 32bit symbol.
         MOV ECX,[EBX+SYM.Section]
         JECXZ .p2:
         MOV ECX,[ECX+SSS.SegmPtr]
         JECXZ .p2:
         MOV ECX,[ECX+SSS.SegmIndex]
     .p2:Invoke PfomfStoreIndex,[%RecBuf],0                ; Base group index.
         Invoke PfomfStoreIndex,[%RecBuf],ECX              ; Base segment index.
         TEST ECX
         JNZ .p3:                                          ; Skip base frame when base segment is defined.
         BufferStoreWord [%RecBuf],0                       ; Base frame when segment=0.
     .p3:MOV EDI,[EBX+SYM.NamePtr]
         MOV EDX,[EBX+SYM.NameSize]
         Invoke PfomfStoreName,[%RecBuf],EDI,EDX
         BufferStoreWord [%RecBuf],[EBX+SYM.OffsetLow]
         Invoke PfomfStoreIndex,[%RecBuf],0                ; Symbol CodeView datatype is not used.
         Invoke PfomfStoreRecord,[%OutputStream],PUBDEF,[%RecBuf] ; Write PUBDEF record.
     .p8:ListGetNext EBX
         JNZ .p1:
     .p9:RET
         ENDP PfomfStoreModule.PUBDEF16:

    PfomfStoreModule.PUBDEF32:PROC ; Public 32bit symbols definitions.
         MOV EDX,[%Pgm]
         ListGetFirst [EDX+PGM.SymList]                    ; Enumerate program symbols.
         JZ .p9:
     .p1:MOV EBX,EAX
         JNSt [EBX+SYM.Status],symPublic|symExtern,.p8:
         MOV EDX,[EBX+SYM.OffsetHigh]
         MOV EAX,[EBX+SYM.OffsetLow]
         TEST EDX
         Msg cc=NZ,'8527',EBX                              ; Offset of public symbol "!1S" exceeded 4 GB, not suported in OMF.
         JNZ .p8:                                          ; Skip 64bit symbol.
         TEST EAX,0xFFFF_0000
         JZ .p8:                                           ; Skip 16bit symbol (it was already exported in .PUBDEF16.
         SUB EDI,EDI                                       ; Base group index if symbol is in no group.
         MOV ECX,[EBX+SYM.Section]
         JECXZ .p2:
         MOV ECX,[ECX+SSS.SegmPtr]
         JECXZ .p2:
         MOV ECX,[ECX+SSS.SegmIndex]
     .p2:Invoke PfomfStoreIndex,[%RecBuf],0                ; Base group index.
         Invoke PfomfStoreIndex,[%RecBuf],ECX              ; Base segment index.
         TEST ECX
         JNZ .p3:                                          ; Skip base frame when base segment is defined.
         BufferStoreWord [%RecBuf],0                       ; Base frame when segment=0
     .p3:MOV EDI,[EBX+SYM.NamePtr]
         MOV EDX,[EBX+SYM.NameSize]
         Invoke PfomfStoreName,[%RecBuf],EDI,EDX
         BufferStoreDword [%RecBuf],[EBX+SYM.OffsetLow]
         Invoke PfomfStoreIndex,[%RecBuf],0                ; Symbol CodeView datatype is not used.
         Invoke PfomfStoreRecord,[%OutputStream],PUBDEF32,[%RecBuf] ; Write PUBDEF record.
     .p8:ListGetNext EBX
         JNZ .p1:
     .p9:RET
         ENDP PfomfStoreModule.PUBDEF32:

    PfomfStoreModule.MODEND:PROC ; Module end marker, entry definition.
         MOV EDX,[%Pgm]
         LEA EDI,[EDX+PGM.EntryExp]
         MOV ECX,[EDX+PGM.Pgmopt.EntrySize]
         JECXZ .e0:
         Invoke PgmEvalEntry::,EDX
         Invoke ExpReportError::,EDI
         JC .NoEntry:                                      ; On error emit no entry.
    .e0: MOV ECX,[EDI+EXP.Status]
         LEA EBX,[EDI+EXP.Low]                             ; EBX is now pointer to the displacement.
         JECXZ .NoEntry:
         Dispatch CL,'A','N','F'                           ; EDI is valid expression with program entry.
         LEA EAX,[EDX+PGM.Pgmopt.EntryPtr]                 ; Prepare error Msg !1S argument.
         Msg '7711',EAX                                    ; Invalid program entry point "!1S".
    .NoEntry:
         BufferClear [%RecBuf]                             ; Remove any data when ENTRY is absent or wrong and should be aborted.
         BufferStoreByte [%RecBuf],0                       ; Module type=0.
         JMP .e7:
     .F: ; Entry is specified as immediate far pointer, e.g. ENTRY=0x0040:1234h.
         MOV ECX,[EDI+EXP.Seg]
     .F1:Msg '2921',EAX                                    ; Nonrelocable entry point "!1S" is not supported by many linkers.
         BufferNew [%RecBuf],6
         MOVW [EAX+0],0x33C1                               ; Module type C1,F3,T3,explicit frame segment value with displacement.
         MOV [EAX+2],CX                                    ; Explicit frame segment value.
         MOV [EAX+4],CX                                    ; Explicit target segment value.
         MOV EAX,[EBX]                                     ; Displacement (entry offset).
         TEST EAX,0xFFFF_0000
         JNZ .e6:                                          ; 32bit displacement.
         JMP .e7:                                          ; 16bit displacement.
     .N: ; Entry is specified as absolute offset (no segment), e.g. ENTRY=100h.
         SUB ECX,ECX
         JMP .F1:
     .A: ; Entry is specified as a symbol, e.g. ENTRY=Start:
         BufferNew [%RecBuf],2
         MOVB [EAX+0],0xC1                                 ; Module type.
         MOV ESI,[EDI+EXP.Seg]                             ; Section or segment of entry point.
         MOV ECX,[ESI+SSS.GroupPtr]
         JECXZ .e2:
         ; Entry is in a grouped segment.
         MOVB [EAX+1],0x10                                 ; FixData F1,T0,displacement.
         Invoke PfomfStoreIndex,[%RecBuf],[ECX+SSS.SegmIndex] ; Frame group.
         Invoke PfomfStoreIndex,[%RecBuf],[ESI+SSS.SegmIndex] ; Target segment.
         JMP .e6:                                          ; When GROUP is used, DWORD displacement is not supported.
     .e2: ; Entry is in nongrouped segment.
         MOVB [EAX+1],0x00                                 ; FixData F0,T0,displacement.
         JSt [ESI+SSS.Status],sssSegment,.e3:
         LEA EAX,[EDX+PGM.Pgmopt.EntryPtr]                 ; Prepare error Msg !1S argument.
         Msg '7713',EAX                                    ; Unresolved external entry point "!1S". Ignored.
         JMP .NoEntry:
     .e3:Invoke PfomfStoreIndex,[%RecBuf],[ESI+SSS.SegmIndex] ; Frame segment.
         Invoke PfomfStoreIndex,[%RecBuf],[ESI+SSS.SegmIndex] ; Target segment.
     .e4:JSt [ESI+SSS.Status],sssWidth16, .e6:
         BufferStore [%RecBuf],EBX,4                       ; DWORD displacement.
         JMP .e7:
     .e6:BufferStore [%RecBuf],EBX,2                       ; WORD displacement.
     .e7:JNSt [EDX+PGM.Status],pgmIsModule,.e9:
         BufferRetrieve [%RecBuf]                          ; MODEND will padd the module to OWORD.
         StreamGetSize [%OutputStream]                     ; Let EAX=bytes emitted so far.
         LEA EAX,[EAX+ECX+4]
         NEG EAX
         MOV ECX,0x0000_000F
         AND ECX,EAX                                       ; Size of OWORD alignment stuff.
         JZ .e9:
     .e8:BufferStoreByte [%RecBuf],0
         LOOP .e8:
     .e9:JNSt [EDX+PGM.Pgmopt.Status],pgmoptWidth32,.eA:
         Invoke PfomfStoreRecord,[%OutputStream],MODEND32,[%RecBuf]
         RET
     .eA:Invoke PfomfStoreRecord,[%OutputStream],MODEND,[%RecBuf]
         RET
         ENDP PfomfStoreModule.MODEND:

↑ PfomfLoadName NameBuf, IndexPtr, Pgm
  PfomfLoadName will find a Pascal string with segment/group name referred by IndexPtr, save it on Pgm.Pool and return the nonvolatile name.
Input
  NameBuf is pointer to BUFFER with dwords pointing to volatile Pascal names in OMF file. The 1st pointer corresponds to the name with index 1.
  IndexPtr is pointer to 1 or 2 bytes index in OMF record.
  Pgm is pointer to the program which the name belongs to.
Output
  EAX= points to nonvolatile name in Pgm.Pool.
  ECX= is the name size in bytes. Zero if IndexPtr pointed to 0.
  ESI= points behind the index in OMF record. It is 1 or 2 bytes above IndexPtr.
Error
  -
Invoked by
  PfomfLoadModule

    PfomfLoadName Procedure NameBuf, IndexPtr, Pgm
         MOV ESI,[%IndexPtr]
         SUB EAX,EAX
         MOV [%ReturnEAX],EAX
         MOV [%ReturnECX],EAX
         LODSB
         TEST AL,80h
         JZ .20:
         XOR AL,80h
         XCHG AH,AL
         LODSB
    .20: MOV [%ReturnESI],ESI
         TEST EAX
         JZ .90: ; Index 0 represents no name.
         BufferRetrieve [%NameBuf]
         SHR ECX,2
         CMP EAX,ECX
         JA .90: ; If wrong index.
         DEC EAX
         MOV ESI,[ESI+4*EAX]
         SUB EAX,EAX
         LODSB ; Pascal string size.
         MOV ECX,EAX
         JECXZ .90:
         MOV EDX,[%Pgm]
         PoolNew [EDX+PGM.Pool],ECX
         MOV [%ReturnEAX],EAX
         MOV [%ReturnECX],ECX
         XCHG EAX,EDI
         REP MOVSB
    .90: EndProcedure PfomfLoadName

↑ PfomfLoadRecord RecPtr, FileStart, FileEnd, FNptr
  PfomfLoadRecord will extract one OMF record identified by %RecPtr, and check its size and checksum.
Input
  RecPtr is pointer to the start of OMF record. Always between FileStart and FileEnd.
  FileStart is pointer to the start of object file mapped in memory.
  FileEnd is pointer where the object/library file ends.
  FNptr is pointer to ASCIIZ filename. It may be NULL, no error message is reported then.
Output
  CF=0 if the record and checksum is formally OK.
  ESI= points to the start of record (identical with input %RecPtr)
  ECX= record brutto size. ESI+ECX points to the next record. ECX=0 when end of file.
Error
  CF=1 on checksum error or wrong record size.
  E8532 "Wrong checksum of OMF record" is reported. ESI,ECX undefined.
Invoked by
  PfDetect PflibomfLoadPgm PfomfLoadModule

    PfomfLoadRecord Procedure RecPtr, FileStart, FileEnd, FNptr
        MOV ESI,[%RecPtr]
        MOV ECX,[%FileEnd]
        MOV [%ReturnESI],ESI
        SUB ECX,ESI
        JZ .20:
        JB .E8532:
        CMP ECX,4
        JB .E8532:
        MOVZXW ECX,[ESI+1]
        ADD ECX,3 ; Brutto record size.
    .20:MOV [%ReturnECX],ECX
        JECXZ .90:
        LEA EDX,[ESI+ECX]
        CMP EDX,[%FileEnd]
        JA .E8532:
        DEC ECX ; Omit the checksum byte.
        MOV AH,[ESI+ECX] ; Load checksum byte.
        TEST AH
        JZ .90: ; Checksum 0 is tolerated.
    .50:LODSB
        ADD AH,AL
        LOOP .50:
        TEST AH
        JZ .90: ; Checksum is OK.
    .E8532:
        MOV ESI,[%RecPtr]
        MOV AL,[ESI]
        SUB ESI,[%FileStart]
        MOV ECX,[%FNptr]
        JECXZ .80:
        Msg '8532',EAX,ECX,ESI ; Wrong checksum of OMF record type !1Bh at "!2$"[!3Hh].
    .80:STC
    .90:EndProcedure PfomfLoadRecord

↑ PfomfLoadSegment SegdefBuf, IndexPtr
  PfomfLoadSegment will find a segment referred by the index at IndexPtr.
Input
  SegdefBuf is pointer to BUFFER with dwords pointing to ^SSS object. The 1st pointer corresponds to segment with index 1.
  IndexPtr is pointer to 1 or 2 byte index in OMF record.
Output
  CF=0
  EAX= points to ^SSS object sssSegment.
  ESI= points behind the index in OMF record. It is 1 or 2 bytes above IndexPtr.
Error
  CF=1, EAX=0 if segment not found.
Invoked by
  PfomfLoadFixData PfomfLoadModule

    PfomfLoadSegment Procedure SegdefBuf, IndexPtr
         MOV ESI,[%IndexPtr]
         SUB EAX,EAX
         MOV [%ReturnEAX],EAX
         LODSB
         TEST AL,80h
         JZ .20:
         XOR AL,80h
         XCHG AH,AL
         LODSB
    .20: MOV [%ReturnESI],ESI
         TEST EAX
         STC
         JZ .90: ; Index 0 represents no segment.
         BufferRetrieve [%SegdefBuf]
         SHR ECX,2
         CMP ECX,EAX
         JC .90: ; If wrong index.
         DEC EAX
         MOV EDX,[ESI+4*EAX]
         MOV [%ReturnEAX],EDX
         CLC
    .90:EndProcedure PfomfLoadSegment

↑ PfomfLoadGroup GrpdefBuf, IndexPtr
  PfomfLoadGroup will find a group referred by index at IndexPtr.
Input
  GrpdefBuf is pointer to BUFFER with dwords pointing to ^SSS object. The 1st pointer corresponds to group with index 1.
  IndexPtr is pointer to 1 or 2 byte index in OMF record.
Output
  CF=0
  EAX= points to ^SSS object sssGroup.
  ESI= points behind the index in OMF record. It is 1 or 2 bytes above IndexPtr.
Error
  CF=1, EAX=0 if group not found.
Invoked by
  PfomfLoadFixData PfomfLoadModule

    PfomfLoadGroup Procedure GrpdefBuf, IndexPtr
         MOV ESI,[%IndexPtr]
         SUB EAX,EAX
         MOV [%ReturnEAX],EAX
         LODSB
         TEST AL,80h
         JZ .20:
         XOR AL,80h
         XCHG AH,AL
         LODSB
    .20: MOV [%ReturnESI],ESI
         TEST EAX
         STC
         JZ .90: ; Index 0 represents no group.
         BufferRetrieve [%GrpdefBuf]
         SHR ECX,2
         CMP ECX,EAX
         JC .90: ; If wrong index.
         DEC EAX
         MOV EDX,[ESI+4*EAX]
         MOV [%ReturnEAX],EDX
         CLC
    .90:EndProcedure PfomfLoadGroup

↑ PfomfLoadExtern ExtdefBuf, IndexPtr
  PfomfLoadExtern will find an extern pseudosegment referred by index at IndexPtr.
Input
  ExtdefBuf is pointer to BUFFER with dwords pointing to ^SSS object. The 1st pointer corresponds to extern with index 1.
  IndexPtr is pointer to 1 or 2 byte index in OMF record.
Output
  CF=0
  EAX= points to ^SSS object sssExtern.
  ESI= points behind the index in OMF record. It is 1 or 2 bytes above IndexPtr.
Error
  CF=1, EAX=0 if extern not found.
Invoked by
  PfomfLoadFixData PfomfLoadModule

    PfomfLoadExtern Procedure ExtdefBuf, IndexPtr
         MOV ESI,[%IndexPtr]
         SUB EAX,EAX
         MOV [%ReturnEAX],EAX
         LODSB
         TEST AL,80h
         JZ .20:
         XOR AL,80h
         XCHG AH,AL
         LODSB
    .20: MOV [%ReturnESI],ESI
         TEST EAX
         STC
         JZ .90: ; Index 0 represents no group.
         BufferRetrieve [%ExtdefBuf]
         SHR ECX,2
         CMP ECX,EAX
         JC .90: ; If wrong index.
         DEC EAX
         MOV EDX,[ESI+4*EAX]
         MOV [%ReturnEAX],EDX
         CLC
    .90:EndProcedure PfomfLoadExtern

↑ PfomfLoadFixData FixDataPtr, RelocPtr, ThreadTabPtr, LastSeg, SegdefBuffer, GrpdefBuffer, ExtdefBuffer, Status32
  PfomfLoadFixData is used to set `RELOC.Symbol, .Frame, .Addend` in relocation record.
Input
  FixDataPtr is a pointer to FixData byte, optionally followed by frame index, target index, target displacement.
  RelocPtr is pointer to an output RELOC object, allocated by the caller. It should have already set RELOC.Org, .Section, .Status.
  ThreadTabPtr is pointer to PFOMF_THREAD_TABLE which keeps previously defined target and frame threads.
  LastSeg is pointer to SSS segment of the previous LEDATA/LIDATA record.
  SegdefBuffer is pointer to the buffer with indexed pointers to SSS segment.
  GrpdefBuffer is pointer to the buffer with indexed pointers to SSS group.
  ExtdefBuffer is pointer to the buffer with indexed pointers to SSS extern.
  Status32 specifies in bit 0 whether the displacement is 16bit or 32bit.
Output
  CF=0, ESI points to the end of fixup subrecord, i.e. behind FixData, Frame Index, TargetIndex, Target displacement.
  Relocation at RelocPtr was updated with specified members `.Symbol, .Addend`.
Error
  CF=1, caller shoud raise E8533 Invalid OMF record.
Invoked by
  PfomfLoadModule
Invokes
  PfomfLoadExtern PfomfLoadGroup PfomfLoadSegment

    PfomfLoadFixData Procedure FixDataPtr, RelocPtr, ThreadTabPtr, LastSegPtr,SegdefBuffer, GrpdefBuffer, ExtdefBuffer, Status32
        MOV ESI,[%FixDataPtr]
        MOV EDX,[%RelocPtr]
        MOV EBX,[%ThreadTabPtr]
        LODSB                                              ; AL=FixData byte.
        SUB ECX,ECX
        ; Find the frame.
        TEST AL,1000_0000b                                 ; FixData.F bit specifies the thread.
        JZ .10:
        ; F=1, frame is specified by previous frame thread at EBX.
        MOV CL,0011_0000b                                  ; Isolate thread number.
        AND CL,AL
        SHR ECX,2
        MOV EDI,[EBX+0+ECX]                                ; Pointer to the segment refered by frame thread ECX.
        JMP .30:
    .10: ; F=0, frame is specified by method F0..F5 in this subrecord.
         MOV CL,0111_0000b
         AND CL,AL
         XCHG EAX,EDI                                      ; Temporary save FixData AL.
         Dispatch CL,pfomfFxF5,pfomfFxF0,pfomfFxF1,pfomfFxF2,pfomfFxF3,pfomfFxF4
         JMP .Error:
    .pfomfFxF0:Invoke PfomfLoadSegment,[%SegdefBuffer],ESI ; Frame is specified by SEGDEF index.
         JMP .20:
    .pfomfFxF1:Invoke PfomfLoadGroup,[%GrpdefBuffer],ESI   ; Frame is specified by GRPDEF index.
         JMP .20:
    .pfomfFxF2:Invoke PfomfLoadExtern,[%ExtdefBuffer],ESI  ; Frame is specified by EXTDEF index.
         JMP .20:
    .pfomfFxF4:MOV EAX,[%LastSegPtr]                       ; Frame is specified by previous LEDATA segment.
         JMP .20:
    .pfomfFxF3:LODSW                                       ; Frame is specified by an absolute PARA address. Not supported.
         JMP .20:
    .pfomfFxF5:XOR EAX,EAX                                 ; Zero signalizes that the frame is identical with target.
    .20: XCHG EDI,EAX                                      ; Move the loaded frame segment to EDI, restore FixData to AL.
         JC .Error:                                        ; If wrong index (PfomfLoad* failed).
    .30: TEST EDI
         JZ .35:                                           ; Skip when frame=0, i.e. specified by the target.
         JNSt [EDI+SSS.Status],sssExtern,.35:
         MOV EDI,[EDI+SSS.SymPtr]
         MOV [EDX+RELOC.Symbol],EDI
         XOR EDI,EDI
    .35: ;;;;;;;; MOV [EDX+RELOC.Frame],EDI                         ; Frame is now specified.
         ; Find the target.
         TEST AL,0000_1000b
         JZ .40:
         ; T=1, target is specified by the previous target thread.
         MOV CL,0000_0011b                                 ; Target field.
         AND CL,AL                                         ; ECX is now target thread number 0..3.
         MOV EDI,[EBX+16+4*ECX]                            ; Pointer to the segment referred by target thread ECX.
         JMP .60:
    .40: ; T=0, target is specified by method T0..T6 in this subrecord.
         MOV CL,00000_011b                                 ; Target method mask pfomfFxT0..pfomfFxT3.
         AND CL,AL
         XCHG EDI,EAX                                      ; Temporary save FixData to EDI.
         Dispatch CL,pfomfFxT0,pfomfFxT1,pfomfFxT2
         JMP .Error:                                       ; If wrong method.
    .pfomfFxT0:Invoke PfomfLoadSegment,[%SegdefBuffer],ESI ; Target is specified by SEGDEF index.
         JMP .50:
    .pfomfFxT1:Invoke PfomfLoadGroup,[%GrpdefBuffer],ESI   ; Target is specified by GRPDEF index.
         JMP .50:
    .pfomfFxT2:Invoke PfomfLoadExtern,[%ExtdefBuffer],ESI  ; Target is specified by EXTDEF index.
        ;JMP .50:
    .50: XCHG EAX,EDI                                      ; Move the loaded target segment to EDI, restore FixData to AL.
         JC .Error:                                        ; If wrong index (PfomfLoad* failed).
    .60: ;;;MOV [EDX+RELOC.Frame],EDI                         ; Frame of target segment is now specified. FixData is in AL.
         MOV EDI,[EDI+SSS.SymPtr]
         MOV [EDX+RELOC.Symbol],EDI
         TEST AL,0000_0100b
         MOV EAX,0
         JNZ .80:                                          ; If P=1, no displacement is present. RELOC.Addend=0.
         ; Load the target displacement (signed WORD or DWORD at ESI).
         JSt [%Status32],1,.70:
         LODSW                                             ; Displacement is a signed WORD in 16bit OMF.
         MOVSX EAX,AX
         JMP .80:
    .Error:STC
         JMP .90:
    .70: LODSD                                             ; Displacement is a signed DWORD in 32bit OMF.
    .80: JNSt [EDX+RELOC.Status],relocRel,.85:
         SUB EAX,2                                         ; IP-relative relocation OMF correction.
         JNSt [%Status32],1,.85:
         SUB EAX,2                                         ; EIP-relative relocation OMF correction.
    .85: MOV EDI,EDX
         CDQ
         MOV [EDI+RELOC.AddendLow],EAX
         MOV [EDI+RELOC.AddendHigh],EDX
    .90: MOV [%ReturnESI],ESI                              ; ESI points behind the target's displacement.
       EndProcedure PfomfLoadFixData

↑ PfomfLoadDataBlock DbStart, DbPtr, DbEnd, OutEmitBuffer, InpRelocBuffer, OutRelocBuffer,StatusPtr
  PfomfLoadDataBlock will expand iterated DataBlock from OMF LIDATA/LIDATA32 record.
  DataBlock consists of members:
  1. RepeatCount (WORD or DWORD)
  2. BlockCount (WORD)
  3. BlockBody
  BlockBody is either Pascal-string with emitted data (if BlockCount=0), or BlockCount number of another nested DataBlocks. The whole expanded BlockBody is then repeated RepeatCount times.
  PfomfLoadDataBlock is invoked recursively when DataBlocks are nested.
Input
  DbStart points to RepeatCount member of the main DataBlock in LIDATA record. RELOC.Org of relocations in InpRelocBuffer is related to DbStart.
  DbPtr points to RepeatCount member of this currently loaded DataBlock. Always between DbStart and DbEnd.
  DbEnd points behind LIDATA record contents (checksum byte).
  OutEmitBuffer is pointer to an empty BUFFER where the data emitted from this DataBlock will be stored.
  InpRelocBuffer is pointer to BUFFER with relocations retrieved from FIXUPP record(s) following LIDATA. Their .Org is related to DbStart.
  OutRelocBuffer is pointer to an empty BUFFER where relocation relevant to this DataBlock data will be stored. Their RELOC.Org will be related to the first byte of expanded data in OutEmitBuffer.
  StatusPtr specifies with flag `pfomfRecLi32` whether the RepeatCount is 16bit or 32bit.
Output
  CF=0 OutEmitBuffer and OutRelocBuffer are populated.
  ESI= points behind the just parsed DataBlock.
Error
  CF=1
  ESI=DbEnd. Caller should report E8533 Invalid OMF record.
Invoked by
  PfomfLoadDataBlock PfomfLoadModule
Invokes
  EaBufferRelease EaBufferReserve PfomfLoadDataBlock RelocRelocInBuffer

    PfomfLoadDataBlock Procedure DbStart,DbPtr,DbEnd,OutEmitBuffer,InpRelocBuffer,OutRelocBuffer,StatusPtr
    RepeatCount   LocalVar ; DataBlock members.
    BlockCount    LocalVar
    DataStart     LocalVar ; Pointer to netto data inside DataBlock (inside LIDATA record mapped in memory).
    DataSize      LocalVar
    DataEnd       LocalVar
    DbOutEmitBuf  LocalVar ; Temporary local buffers.
    DbInpRelocBuf LocalVar
    DbOutRelocBuf LocalVar
         EaStackCheck
         Invoke EaBufferReserve::,PfomfLoadDataBlock
         MOV [%DbOutEmitBuf],EAX
         Invoke EaBufferReserve::,PfomfLoadDataBlock
         MOV [%DbInpRelocBuf],EAX
         Invoke EaBufferReserve::,PfomfLoadDataBlock
         MOV [%DbOutRelocBuf],EAX
         MOV ESI,[%DbPtr]
         MOV EDX,[%DbEnd]
         SUB EAX,EAX
         SUB EDX,ESI
         CMP EDX,5
         JNA .Error:
         JSt [%StatusPtr],pfomfRecLi32,.10:
         LODSW
         JMP .15:
     .10:LODSD
     .15:MOV [%RepeatCount],EAX
         TEST EAX
         JZ .Error:
         SUB EAX,EAX
         LODSW
         MOV [%BlockCount],EAX
         TEST EAX
         JNZ .70:
         ; BlockCount=0. It is final DataBlock. Pascal data string follows.
         LODSB
         MOV [%DataStart],ESI
         MOV [%DataSize],EAX
         ADD EAX,ESI
         MOV [%DataEnd],EAX                          ; ESI..EAX specifies netto data in LIDATA record, perhaps subjected to fixup.
         MOV [%ReturnESI],EAX
         ; Inspect whether origin of some relocation(s) in %InpRelocBuffer concerns our netto data %DataStart..%DataEnd
         ;   and copy such relocations to %DbInpRelocBuf.
         BufferRetrieve [%InpRelocBuffer]
         JECXZ .50:
     .20:MOV EAX,[ESI+RELOC.OrgLow]                  ; Related to %DbStart.
         ADD EAX,[%DbStart]
         CMP EAX,[%DataStart]
         JB .40:                                     ; Skip this relocation.
         ADD EAX,2
         JNSt [ESI+RELOC.Status],relocWidth32,.30:
         ADD EAX,2
     .30:CMP EAX,[%DataEnd]
         JA .40:                                     ; Skip this relocation.
         ; Relocation ESI concerns this DataBlock.
         BufferStore [%DbInpRelocBuf],ESI,SIZE#RELOC
     .40:ADD ESI,SIZE#RELOC
         SUB ECX,SIZE#RELOC
         JA .20:                                     ; If more than one LIDATA relocation exist.
     .50: ; Relevant relocation(s) were copied to %DbInpRelocBuf.
         MOV EAX,[%DbStart]
         SUB EAX,[%DataStart]
         Invoke RelocRelocInBuffer::,[%DbInpRelocBuf],EAX ; Origins are now related to the start of netto data.
     .60:; Loop to duplicate emit and reloc.
         BufferStore [%DbOutEmitBuf],[%DataStart],[%DataSize]
         BufferRetrieve [%DbInpRelocBuf]
         BufferStore [%DbOutRelocBuf],ESI,ECX
         Invoke RelocRelocInBuffer::,[%DbInpRelocBuf],[%DataSize] ; Prepare RELOC.Org for the next RepeatCount.
         DECD [%RepeatCount]
         JNZ .60:
         ; Export emitted data and relocations.
         BufferRetrieve [%OutEmitBuffer]
         MOV EDX,ECX                                 ; Parent Db emitted size.
         Invoke RelocRelocInBuffer::,[%DbOutRelocBuf],EDX ; Relocate our relocation(s).
         BufferRetrieve [%DbOutEmitBuf]              ; Our emitted data.
         BufferStore [%OutEmitBuffer],ESI,ECX        ; Append to parent's.
         BufferRetrieve [%DbOutRelocBuf]
         BufferStore [%OutRelocBuffer],ESI,ECX       ; Append our relocations to parent's.
         JMP .90: ; CF=0.
    .Error:
         MOV ESI,[%DbEnd]
         MOV [%ReturnESI],ESI
         STC
         JMP .90:
     .70: ; BlockCount > 0. Another DataBlocks follow at ESI.
         Invoke PfomfLoadDataBlock,[%DbStart],ESI,[%DbEnd],[%DbOutEmitBuf],[%InpRelocBuffer],[%DbInpRelocBuf],[%StatusPtr]
         JC .Error:
         DECD [%BlockCount]
         JNZ .70:
     .80:; Start to duplicate emit and reloc RepeatCount times.
         BufferRetrieve [%DbOutEmitBuf]
         MOV EDX,ECX ; DataBlock emitted size.
         BufferStore [%OutEmitBuffer],ESI,ECX
         BufferRetrieve [%DbInpRelocBuf]
         BufferStore [%OutRelocBuffer],ESI,ECX
         Invoke RelocRelocInBuffer::,[%DbInpRelocBuf],EDX ; Prepare relocations for the next repeat.
         DECD [%RepeatCount]
         JNZ .80:
     .90:PUSHFD
          Invoke EaBufferRelease::,[%DbOutEmitBuf]
          Invoke EaBufferRelease::,[%DbInpRelocBuf]
          Invoke EaBufferRelease::,[%DbOutRelocBuf]
         POPFD
     EndProcedure PfomfLoadDataBlock

↑ PfomfLoadPgm BasePgm, ObjBegin, ObjSize, FileNamePtr
  PfomfLoadPgm reads the contents of one object file (module) in OMF format and converts it to internal PGM structure which then will be stored on `Pgm.ModulePgmList` and marked as `pgmUsed`.
  The module starts with THEADR record and it terminates with MODEND or MODEND32 record.

  The actual job is performed by PfomfLoadModule, a common procedure used by PflibomfLoadPgm and PfomfLoadPgm.

Input
  BasePgm is pointer to an existing PGM to which the object file is being linked/imported.
  ObjBegin is pointer to the contents of linked object file mapped in memory by the caller.
  ObjSize is number of bytes in the object file.
  FileNamePtr is pointer to zero-terminated object file name (used in error reports).
Output
  Loaded program is stored on `BasePgm.ModulePgmList`.
Error
  Errors are reported with macro Msg.
Invoked from
  PfLoad
Invokes
  PfomfLoadModule
Tested by
  t7921 t7922 t7923 t7924 t7925 t7926

    PfomfLoadPgm Procedure BasePgm, ObjBegin, ObjSize, FileNamePtr
        MOV EBX,[%BasePgm]
        MOV ESI,[%ObjBegin]
        MOV ECX,[%ObjSize]
        LEA EDX,[ESI+ECX]
        Invoke PfomfLoadModule,EBX,ESI,ESI,EDX,[%FileNamePtr],pgmoptOMF
        JZ .90:
        ; Unlike library modules from format LIBOMF, object files in format OMF explicitly requested with
        ; LINK "file.obj" will be always selected, i.e. statically linked to BasePgm.
        SetSt [EAX+PGM.Status],pgmSelected
        ; If the module width was not specified, it will be copied from its regular segments.
        MOV EDX,pgmoptWidthMask
        AND EDX,[EAX+PGM.Pgmopt.Status]
        JNZ .90:
        ListGetFirst [EAX+PGM.SssList]
        JZ .90:
    .50:JNSt [EAX+SSS.Status],sssSegment,.70:
        JNSt [EAX+SSS.Purpose],sssPurposeRegular,.70:
        MOV ECX,sssWidthMask
        AND ECX,[EAX+SSS.Status]
        SetSt [EBX+PGM.Pgmopt.Status],ECX
    .70:ListGetNext EAX
        JNZ .50:
    .90:EndProcedure PfomfLoadPgm

↑ PfomfLoadModule BaseProgram, FileBegin, ModBegin, ModEnd, FileName$, Format
  This is a common procedure for the load of linked OMF and LIBOMF format. PfomfLoadModule reads the contents of one OMF module (program) from the file in OMF or LIBOMF format and converts it to internal PGM structure which then will be stored on `BaseProgram.ModulePgmList`. The module starts with THEADR or LHEADR record and it terminates with MODEND record.
Input
  BaseProgram is pointer to an existing PGM to which the object file/library module is being linked/imported.
  FileBegin is pointer to the start of OMF or LIBOMF file mapped in memory.
  ModBegin is pointer to the first OMF record which starts the module (THEADR or LHEADR) mapped in memory by the caller. It is always between FileBegin and ModEnd.
  ModEnd is pointer behind the last module record (MODEND or MODEND32).
  FileName$ is pointer to zero-terminated object file name (used in error reports).
  Format is either pgmoptOMF or pgmoptLIBOMF.
Output
  ZF=0
  EAX=^PGM with the just loaded module. Module is loaded from file, converted to PGM structure and EAX is stored on `BaseProgram.ModulePgmList`.
Error
  ZF=1
  EAX=0 when no module was loaded. Errors are reported with macro Msg.
Invoked by
  PflibomfLoadPgm PfomfLoadPgm
Invokes
  EaBufferRelease EaBufferReserve PfDrectveDestroy PfomfLoadDataBlock PfomfLoadExtern PfomfLoadFixData PfomfLoadGroup PfomfLoadName PfomfLoadRecord PfomfLoadSegment PgmCreateImportModule PgmDetectImportModule PgmoptSetLinkProp RelocRelocInBuffer SssCreateExtern SssGuessPurpose SymCreateSe SymFindByInterName SymFindByName

    PfomfLoadModule Procedure BaseProgram, FileBegin, ModBegin, ModEnd, FileName$, Format
    ModPtr           LocalVar ; Pointer to the next OMF record.
    RecPtr           LocalVar ; Pointer to the currently processed OMF record.
    DataPtr          LocalVar ; Pointer to the volatile LEDATA data withing the record.
    DataSize         LocalVar ; Size of data at %DataPtr.
    DataOffs         LocalVar ; Pointer to the recent LEDATA data within the EmitBuffer.
    LastSegm         LocalVar ; Pointer to the recent SSS segment of LEDATA.
    NamePtr          LocalVar ; Pointer to the recent symbol name.
    NameSize         LocalVar ; Size of the recent symbol name.
    DllNamePtr       LocalVar ; Pointer to the recent DLL name.
    DllNameSize      LocalVar ; Size of the recent DLL name.
    InterNamePtr     LocalVar ; Pointer to the recent internal import name.
    InterNameSize    LocalVar ; Size of the recent internal import name.
    OrdinalNr        LocalVar ; Ordinal number if imported symbol or 0 when imported by name.
    LnamesBuf        LocalVar ; Pointer to BUFFER with dword pointers to Pascal string with SSS name (volatile).
    SegdefBuf        LocalVar ; Pointer to BUFFER with dword pointers to SSS segments located on program pool.
    GrpdefBuf        LocalVar ; Pointer to BUFFER with dword pointers to SSS groups   located on program pool.
    ExtdefBuf        LocalVar ; Pointer to BUFFER with dword pointers to SSS externs  located on program pool.
    LiOutEmitBuf     LocalVar ; Pointer to BUFFER with expanded LIDATA data.
    LiInpRelocBuf    LocalVar ; Pointer to BUFFER with relocations in %LiInpEmitBuf.
    LiOutRelocBuf    LocalVar ; Pointer to BUFFER with relocations in %LiOutEmitBuf.
    LiDbStart        LocalVar ; Pointer to the data block in LIDATA record.
    LiDbEnd          LocalVar ; Pointer to the end of data block in LIDATA record (to the checksum byte).
    LdataOffs        LocalVar ; 10bit offset or relocation within LEDATA netto data or within LIDATA DataBlock.
    OmfStatus        LocalVar ; Binary flags, see PfomfEnc.
    Sss              LocalVar Size=SIZE#SSS   ; Currently processed segment or group of linked program.
    Reloc            LocalVar Size=SIZE#RELOC ; Currently processed relocation.
    Sym              LocalVar Size=SIZE#SYM   ; Currently processed symbol of linked program.
    ModulePgm        LocalVar Size=SIZE#PGM   ; Currently processed linked program in the form of PGM object.
    ThreadTab        LocalVar Size=SIZE#PFOMF_THREAD_TABLE ; Frame and target fixup threads 0..3.
        ; Initialize local data structures
        ClearLocalVar
        SUB EAX,EAX
        MOV [%ReturnEAX],EAX
        Invoke EaBufferReserve::,PfomfLoadModule
        MOV [%LnamesBuf],EAX
        Invoke EaBufferReserve::,PfomfLoadModule
        MOV [%SegdefBuf],EAX
        Invoke EaBufferReserve::,PfomfLoadModule
        MOV [%GrpdefBuf],EAX
        Invoke EaBufferReserve::,PfomfLoadModule
        MOV [%ExtdefBuf],EAX
        Invoke EaBufferReserve::,PfomfLoadModule
        MOV [%LiOutEmitBuf],EAX
        Invoke EaBufferReserve::,PfomfLoadModule
        MOV [%LiInpRelocBuf],EAX
        Invoke EaBufferReserve::,PfomfLoadModule
        MOV [%LiOutRelocBuf],EAX
        MOV ESI,[%ModBegin]
        MOV [%ModPtr],ESI
        ; Preinitialize loaded module (PGM object). It will inherit some properties
        ;   from %BaseProgram rather than initializing the whole PGM structure with PgmCreate.
        LEA EDI,[%ModulePgm]
        MOV EBX,[%BaseProgram]
        MOV EDX,[EBX+PGM.Pool]
        MOV [EDI+PGM.Pool],EDX
        ListCreate EDX,SIZE#SYM
        MOV [EDI+PGM.SymList],EAX
        ListCreate EDX,SIZE#SSS
        MOV [EDI+PGM.SssList],EAX
        MOV ESI,[%Format]
        Invoke PgmoptSetLinkProp::,ESI
        CMP ESI,pgmoptOMF
        JE .05:                                      ; Do not set pgmoptLibMember when PfomfLoadModule was
        SetSt EAX,pgmoptLibMember                    ;   invoked by PfomfLoadPgm (and not by PfomflibLoadPgm).
    .05:MOV [EDI+PGM.Pgmopt.Status],EAX
        MOV ESI,[%ModBegin]
        MOV [%ModPtr],ESI                            ; OMF record to start with.
    .NextRecord:                                     ; The main loop: read and handle OMF records.
        MOV EBX,[%BaseProgram]
        MOV ESI,[%ModPtr]
        MOV [%RecPtr],ESI
        Invoke PfomfLoadRecord, ESI,[%FileBegin],[%ModEnd],[%FileName$]
        JC .80:                                      ; Abort if the file is invalid.
        ADD [%ModPtr],ECX                            ; Prepare %ModPtr for the next OMF record.
        JNSt [%OmfStatus],pfomfLiPending,.20:
        JECXZ .10:                                   ; Previous LIDATA record needs expansion, even at the end of module.
        MOV AL,[ESI]                                 ; Type of just loaded OMF record.
        CMP AL,FIXUPP
        JE .20:
        CMP AL,FIXUPP32
        JE .20:                                      ; Do not expand LIDATA while they are followed by their fixups.
    .10:PUSH ECX,ESI
          CALL .ExpandLIDATA:
        POP ESI,ECX
        RstSt [%OmfStatus],pfomfLiPending
    .20:TEST ECX                                     ; OMF record brutto size.
        JZ .70:                                      ; End of module.
        SUB ECX,4                                    ; Omit record type, length, checksum. ECX=0 when the record is empty.
        JB .90:                                      ; Abandon wrong object file.
        SUB EAX,EAX
        LODSB                                        ; Record type.
        ADD ESI,2                                    ; Skip the record size (it was already processed by PfomfLoadRecord).
        LEA EDI,[%ModulePgm]
        Dispatch AL,LNAMES,EXTDEF,LEDATA,LEDATA32,FIXUPP,FIXUPP32,SEGDEF,SEGDEF32,PUBDEF,PUBDEF32, \
                 GRPDEF,COMENT,THEADR,LHEADR,LIDATA,LIDATA32,MODEND,MODEND32
        JMP .NextRecord:                             ; Silently ignore other record types.

     ; Record handlers input:
     ;      EBX=^%BaseProgram,
     ;      EDI=^%ModulePgm (linked program),
     ;      ESI=pointer to the record body (%RecPtr+3),
     ;      ECX=record body netto size (may be 0),
     ;      EAX=record type in PfomfRecTypes encoding.
     ; Handlers may destroy any GPR but EBP. They continue with .NextRecord or abort with .E8533.

    .LHEADR:
    .THEADR:                                         ; ESI,ECX is Pascal string with module name.
       TEST ECX
       JZ .NextRecord:
       LODSB ; Pascal string size.
       DEC ECX
       CMP EAX,ECX
       JA .h2:
       MOV ECX,EAX
     .h2:
       PoolStore [EDI+PGM.Pool],ESI,ECX
       MOV [EDI+PGM.NamePtr],EAX
       MOV [EDI+PGM.NameSize],ECX
       JMP .NextRecord:

    .COMENT:
        CMP ECX,2
        JNA .NextRecord:
        INC ESI                                      ; Ignore COMENT type flags (purge, list).
        LODSB
        Dispatch AL,0x9D,0xA0                        ; Comment class.
        JMP .NextRecord:                             ; Ignore other comment classes.

    .0xA0: ; COMENT class A0 - OMF extensions.
        LODSB                                        ; OMF extension number.
        Dispatch AL,0n1,0n2                          ; Using alternative number notation to avoid conflict in labels.
        JMP .NextRecord:                             ; Ignore other extensions.

    .0n1: ; COMENT class A0 extension 1 - IMPDEF.
        ; Import definition creates symImport in the module EDI.
        RstSt [%OmfStatus],pfomfByOrdinal
        XOR EAX,EAX
        MOV [%OrdinalNr],EAX                         ; First assume import by name.
        LODSB                                        ; A byte which specifies the kind of import(ByOrdinal/ByName).
        TEST AL
        JZ .i1:
        SetSt [%OmfStatus],pfomfByOrdinal
    .i1:LODSB
        MOVZX ECX,AL                                 ; ESI,ECX is now volatile internal imported symbol name, e.g. "__imp_Function".
        MOV [%InterNamePtr],ESI
        MOV [%InterNameSize],ECX
        MOV [%NamePtr],ESI                           ; Prepare for the case when name === internal name.
        MOV [%NameSize],ECX
        ADD ESI,ECX
        LODSB
        MOVZX ECX,AL                                 ; ESI,ECX is now library name e.g. "kernel32.dll".
        MOV [%DllNamePtr],ESI
        MOV [%DllNameSize],ECX
        ADD ESI,ECX
        ; ESI now points to the symbol name or to a word with ordinal number.
        JNSt [%OmfStatus],pfomfByOrdinal,.i2:
        LODSW
        MOV [%OrdinalNr],EAX
        INC ESI                                      ; Skip record checksum.
        CMP ESI,[%ModPtr]                            ; ESI should be at the end of record.
        JE .i5:
    .E8533:MOV ESI,[%RecPtr]                         ; Abort linking due to error in file.
        MOV CL,[ESI]
        SUB ESI,[%FileStart]                         ; Get the file address of bad record.
        Msg '8533',ECX,[%FileName$],ESI              ; Invalid OMF record type !1Bh at "!2$"[!3Hh].
        JMP .80:                                     ; Abort file load.
    .i2:; ESI now points to imported Pascal name.
        LODSB
        MOVZX ECX,AL                                 ; ESI,ECX is now symbol name e.g. "Function". Or is is NULL (identical with InterName).
        JECXZ .i3:
        CMP AL,1
        JNE .i4:
        CMP [ESI],CH                                 ; Is the first character NULL?
        JNE .i4:
    .i3:MOV ESI,[%InterNamePtr]
        MOV ECX,[%InterNameSize]
    .i4:MOV [%NamePtr],ESI
        MOV [%NameSize],ECX
    .i5:; Imported symbol will be stored to the module EDI.
        MOV ESI,[%NamePtr]
        MOV ECX,[%NameSize]
        Invoke SymFindByName::,symImport|symExtern,ESI,ECX,EDI
        MOV EBX,EAX
        JNC .i6:
        ; Symbol named ESI,ECX wasn't found in module. Let's store it there.
        ListNew [EDI+PGM.SymList],Zeroed=Yes
        MOV EBX,EAX
        PoolStore [EDI+PGM.Pool],ESI,ECX             ; Make the new name nonvolatile.
        MOV [EBX+SYM.NamePtr],EAX
        MOV [EBX+SYM.NameSize],ECX
        MOV [EBX+SYM.InterNamePtr],EAX
        MOV [EBX+SYM.InterNameSize],ECX
    .i6:MOV ESI,[%InterNamePtr]
        MOV ECX,[%InterNameSize]
        JECXZ .i7:
        Compare [EBX+SYM.NamePtr],[EBX+SYM.NameSize],ESI,ECX
        JE .i7:
        PoolStore [EDI+PGM.Pool],ESI,ECX
        MOV [EBX+SYM.InterNamePtr],EAX
        MOV [EBX+SYM.InterNameSize],ECX
    .i7:JNSt [%OmfStatus],pfomfByOrdinal,.i8:
        SetSt [EBX+SYM.Status],symImportedByOrd
        MOV EAX,[%OrdinalNr]
        MOV [EDI+SYM.OrdinalNr],EAX
    .i8:MOV ESI,[%DllNamePtr]
        MOV ECX,[%DllNameSize]
        PoolStore [EDI+PGM.Pool],ESI,ECX             ; Make the DLL name nonvolatile.
        MOV [EBX+SYM.DllNamePtr],EAX
        MOV [EBX+SYM.DllNameSize],ECX
        SetSt [EBX+SYM.Status],symImport+symUsed
        MOVB [EBX+SYM.Status],'A'
        Invoke SssCreateExtern::,EBX,EDI
        JMP .NextRecord:

    .0n2: ; COMENT class A0 extension 2 - EXPDEF.
       ; Export definition creates symExport in module EDI.
        RstSt [%OmfStatus],pfomfByOrdinal
        XOR EAX,EAX
        MOV [%OrdinalNr],EAX                         ; First assume import by name.
        LODSB                                        ; A byte which specifies the kind of export(ByOrdinal/ByName).
        TEST AL,0x80
        JZ .x1:
        SetSt [%OmfStatus],pfomfByOrdinal
    .x1:LODSB
        MOVZX ECX,AL                                 ; ESI,ECX is now the volatile exported symbol name.
        MOV [%NamePtr],ESI
        MOV [%NameSize],ECX
        MOV [%InterNamePtr],ESI
        MOV [%InterNameSize],ECX
        ADD ESI,ECX
        LODSB
        MOVZX ECX,AL                                 ; ESI,ECX is now the volatile internal name.
        JECXZ .x3:
        MOV [%InterNamePtr],ESI
        MOV [%InterNameSize],ECX
    .x3:ADD ESI,ECX
        JNSt [%OmfStatus],pfomfByOrdinal,.x4:
        LODSW
        MOV [%OrdinalNr],EAX
    .x4:INC ESI                                      ; Skip record checksum.
        CMP ESI,[%ModPtr]                            ; ESI should be at the end of record.
        JNE .E8533:                                  ; Otherwise abort linking due to error in file.
       ; Exported symbol will be stored to the module EDI.
        MOV ESI,[%NamePtr]
        MOV ECX,[%NameSize]
        Invoke SymFindByName::,symPublic|symExport,ESI,ECX,EDI ; It shouldn't be there.
        Msg cc=NC,'6611',EAX,[%FileName$],[EAX+SYM.LinePtr] ; Symbol "!1S" exported from "!2$" was already declared at !3@.
        ListNew [EDI+PGM.SymList],Zeroed=Yes         ; Create new exported symbol in module EDI.
        MOV EBX,EAX
        PoolStore [EDI+PGM.Pool],ESI,ECX             ; Make the new name nonvolatile.
        MOV [EBX+SYM.NamePtr],EAX
        MOV [EBX+SYM.NameSize],ECX
        MOV EAX,[%InterNamePtr]
        MOV ECX,[%InterNameSize]
        Compare [EBX+SYM.NamePtr],[EBX+SYM.NameSize],EAX,ECX
        JE .x6:
        PoolStore [EDI+PGM.Pool],EAX,ECX
    .x6:MOV [EBX+SYM.InterNamePtr],EAX
        MOV [EBX+SYM.InterNameSize],ECX
    .x7:MOV EAX,[%OrdinalNr]
        MOV [EBX+SYM.OrdinalNr],EAX
        SetSt [EBX+SYM.Status],symExport
        MOVB [EBX+SYM.Status],'A'
        JMP .NextRecord:

    .0x9D: ; COMENT class 9D - memory model.
        INC ESI,ESI
        SUB ECX,2
        JNA .NextRecord:
        XOR EDX,EDX
    .c1:LODSB
        Dispatch AL,'s','m','c','l','h','f'
        LOOP .c1:
        JMP .NextRecord:
    .s: SetSt EDX,pgmoptSMALL
        JMP .c2:
    .m: SetSt EDX,pgmoptMEDIUM
        JMP .c2:
    .c: SetSt EDX,pgmoptCOMPACT
        JMP .c2:
    .l: SetSt EDX,pgmoptLARGE
        JMP .c2:
    .h: SetSt EDX,pgmoptHUGE
        JMP .c2:
    .f: SetSt EDX,pgmoptFLAT
    .c2:OR [EDI+PGM.Pgmopt+PGMOPT.Status],EDX
        JMP .NextRecord:

    .LNAMES:
        TEST ECX
        JZ .NextRecord:
    .n2:PUSH ESI                                     ; Pointer to a Pascal string.
         MOV EAX,ESP
         BufferStore [%LnamesBuf],EAX,4
        POP ESI
        SUB EAX,EAX
        LODSB ; Length of Pascal string.
        DEC ECX
        ADD ESI,EAX
        SUB ECX,EAX
        JNZ .n2:
        JMP .NextRecord:

    .SEGDEF32:
        SetSt [%OmfStatus],pfomfRec32
        JMP .s0:
    .SEGDEF:
        RstSt [%OmfStatus],pfomfRec32
    .s0:CMP ECX,6
        JB .NextRecord:                              ; Record is too short, ignore.
        LEA EDX,[%Sss]
        Clear EDX,Size=SIZE#SSS
        LODSB                                        ; Load segment attributes ACBP.
        JSt [%OmfStatus],pfomfRec32, .s1:
        TEST AL,0000_0010b                           ; Big segment size?
        JZ .s1:
        MOV [EDX+SSS.TopLow],0x0001_0000
    .s1:MOV EDI,sssSegment+sssWidth32
        TEST AL,0000_0001b                           ; USE32?
        JNZ .s2:
        MOV EDI,sssSegment+sssWidth16
    .s2:MOV CL,000_111_00b
        AND CL,AL                                    ; Field C - combine property.
        Dispatch CL,000_000_00b,000_101_00b,000_110_00b
        OR EDI,sssPublic
        JMP .s3:
    .000_000_00b:                                    ; C=0 private.
        OR EDI,sssPrivate
        JMP .s3:
    .000_101_00b:                                    ; C=5 stack.
        OR EDI,sssStack
        SetSt [EDX+SSS.Purpose],sssPurposeSTACK
        JMP .s3:
    .000_110_00b:                                    ; C=6 common.
        OR EDI,sssCommon
       ;JMP .s3:
    .s3:SetSt [EDX+SSS.Status],EDI
        SHR EAX,5                                    ; Field A - alignment property.
        Dispatch AL,2,3,4,5,6
        XOR EDI,EDI
        INC EDI                                      ; Default segment alignment = BYTE.
        JMP .s4:
    .2: MOV EDI,2                                    ; WORD.
        JMP .s4:
    .3: MOV EDI,16                                   ; OWORD.
        JMP .s4:
    .4: MOV EDI,256                                  ; PAGE 256 bytes.
        JMP .s4:
    .5: MOV EDI,4                                    ; DWORD.
        JMP .s4:
    .6: MOV EDI,4K                                   ; PAGE 4K bytes.
       ;JMP .s4:
    .s4:MOV [EDX+SSS.Alignment],EDI
        XOR EAX,EAX                                  ; Load segment size.
        JSt [%OmfStatus],pfomfRec32,.s5:
        LODSW
        JMP .s6:
    .s5:LODSD
    .s6:MOV [EDX+SSS.TopLow],EAX
        Invoke PfomfLoadName,[%LnamesBuf],ESI,[%BaseProgram]  ; Load segment name by index.
        MOV [EDX+SSS.NamePtr],EAX
        MOV [EDX+SSS.NameSize],ECX
        TEST ECX
        JZ  .E8533:                                 ; Name must not be empty.
        Invoke PfomfLoadName,[%LnamesBuf],ESI,[%BaseProgram]  ; Load class name by index.
        MOV [EDX+SSS.ClassPtr],EAX
        MOV [EDX+SSS.ClassSize],ECX
        ; Invoke PfomfLoadName,[%LnamesBuf],ESI,[%BaseProgram] ; Overlay name index (ignored).
        LEA EDI,[%ModulePgm]
        MOV EBX,[EDI+PGM.Pool]
        MOV [EDX+SSS.PgmPool],EBX
        MOV ECX,[EDX+SSS.TopLow]
        ADD ECX,32                                   ; Estimate EmitBuffer size.
        BufferCreate EBX,Size=ECX
        MOV [EDX+SSS.EmitBuffer],EAX
        SAR ECX,1                                    ; Estimate RelocBuffer size.
        ADD ECX,32
        BufferCreate EBX,Size=ECX
        MOV [EDX+SSS.RelocBuffer],EAX
        Invoke SssGuessPurpose::,EDX
        ListStore [EDI+PGM.SssList],EDX              ; Save the segment into the loaded/imported module EDI.
        MOV [EAX+SSS.SegmPtr],EAX
        Invoke SymCreateSe::,EAX,EDI
        MOV [EAX+SSS.SegmPtr],EAX
        BufferStoreDword [%SegdefBuf],EAX
        JMP .NextRecord:

    .GRPDEF:
        LEA EDX,[%Sss]
        Clear EDX,Size=SIZE#SSS
        SetSt [EDX+SSS.Status],sssGroup+sssWidth16+sssPublic
        Invoke PfomfLoadName,[%LnamesBuf],ESI,[%BaseProgram] ; Load group name by index.
        MOV [EDX+SSS.NamePtr],EAX
        MOV [EDX+SSS.NameSize],ECX
        TEST ECX
        JZ .E8533:                                   ; Name must not be empty.
        LEA EDI,[%ModulePgm]
        ListStore [EDI+PGM.SssList],EDX
        MOV [EAX+SSS.GroupPtr],EAX
        Invoke SymCreateSe::,EAX,EDI
        PUSH EAX
          MOV EAX,ESP
          BufferStore [%GrpdefBuf],EAX,4
        POP EBX                                      ; Ptr to the group on linked Pgm.SssList.
    .g2:MOV ECX,[%ModPtr]                            ; End of this record.
        SUB ECX,3
        CMP ESI,ECX
        JA .NextRecord:
        LODSB                                        ; Load 0xFF marker.
        CMP AL,0xFF
        JNE .g2:
        Invoke PfomfLoadSegment,[%SegdefBuf],ESI     ; Load segment pointer by index.
        JC .E8533:
        MOV [EAX+SSS.GroupPtr],EBX                   ; Add the segment EAX to group EBX.
        JMP .g2:

    .EXTDEF:
        XOR EAX,EAX
        MOV [%DllNamePtr],EAX
        MOV [%DllNameSize],EAX
    .e1:MOV EAX,[%ModPtr]
        DEC EAX
        CMP ESI,EAX
        JNB .NextRecord:                             ; If there are no more external definitions in EXTDEF.
        LODSB                                        ; External name size.
        MOVZX ECX,AL                                 ; ESI,ECX is now and external symbol name or imported external symbol internal name.
        TEST ECX
        JZ .E8533:
        LEA EBX,[%ModulePgm]
        Invoke SymFindByName::,symExtern|symImport,ESI,ECX,EBX
        MOV EDX,EAX
        JNC .e4:                                     ; Skip if external symbol already exists in loaded module.
        Invoke SymFindByInterName::,symExtern|symImport,ESI,ECX,EBX
        MOV EDX,EAX
        JNC .e4:
        CALL .ExternSymbolAndSegmentCreate:
    .e4:; EDX is now the extern symbol with name ESI,ECX.
        MOV EDI,[EDX+SYM.Section]                    ; Extern pseudosection was already stored in this module.
        ADD ESI,ECX                                  ; Skip symExtern name in EXTDEF record.
        MOV EAX,[%OrdinalNr]
        MOV [EDX+SYM.OrdinalNr],EAX                  ; Update extern symbol in case it's defined within IMPDEF record.
        JSt [EDX+SYM.Status],symImport,.e6:
        MOV EAX,[%DllNamePtr]
        MOV ECX,[%DllNameSize]
        MOV [EDX+SYM.DllNamePtr],EAX
        MOV [EDX+SYM.DllNameSize],ECX
    .e6:PUSH EDI ; External pseudosection.
          MOV EAX,ESP
          BufferStore [%ExtdefBuf],EAX,4            ; Pointer to sssExtern.
        POP EDI
    .e9:LODSB                                       ; Skip unused type index.
        TEST AL,0x80
        JZ .e1:
        LODSB
        JMP .EXTDEF:                                ; More extern definitions may follow.

    .PUBDEF32:
        SetSt [%OmfStatus],pfomfRec32
        JMP .p0:
    .PUBDEF:
        RstSt [%OmfStatus],pfomfRec32
    .p0:CMP ECX,7
        JB .NextRecord:                              ; Record is too short, ignore.
        LEA EDX,[%Sym]
        Clear EDX,Size=SIZE#SYM                      ; Public record creates symPublic.
        Invoke PfomfLoadGroup,[%GrpdefBuf],ESI       ; Group is ignored.
        Invoke PfomfLoadSegment,[%SegdefBuf],ESI
        MOV [EDX+SYM.Section],EAX
        TEST EAX
        JNZ .p2:
        ADD ESI,2                                    ; Skip Base frame when the public symbol is at absolute segment.
    .p2:MOV EAX,[%ModPtr]                            ; The next OMF record.
        SUB EAX,6
        CMP ESI,EAX
        JNB .NextRecord:                             ; If no more public definitions in the record.
        LODSB                                        ; Public name size.
        MOVZXB ECX,AL                                ; ESI,ECX is now the public name.
        PoolStore [EDI+PGM.Pool],ESI,ECX
        ADD ESI,ECX                                  ; ESI is now at the offset behind the symbol name.
        MOV [EDX+SYM.NamePtr],EAX
        MOV [EDX+SYM.NameSize],ECX
        SetSt [EDX+SYM.Status],symPublic
    .p5:JSt [%OmfStatus],pfomfRec32,.p7:
        XOR EAX,EAX
        LODSW
        JMP .p8:
    .p7:LODSD
    .p8:MOV [EDX+SYM.OffsetLow],EAX
        LEA EDI,[%ModulePgm]
        ListStore [EDI+PGM.SymList],EDX
        LODSB ; Skip unused type index.
        TEST AL,0x80
        JZ .p2:
        LODSB
        JMP .p2:

    .LEDATA32:
        SetSt [%OmfStatus],pfomfRec32
        JMP .l0:
    .LEDATA:
        RstSt [%OmfStatus],pfomfRec32
    .l0:MOV EDX,ESI                                  ; Temporary save start of the record position.
        Invoke PfomfLoadSegment,[%SegdefBuf],ESI
        JC .E8533:
        MOV EDI,EAX                                  ; Segment which the data are emitted to.
        SetSt [EDI+SSS.Status],sssNotBSS
        JSt [EDI+SSS.Purpose],sssPurposeCODE|sssPurposeRODATA|sssPurposeDATA|sssPurposeDRECTVE,.l2:
        SetSt [EDI+SSS.Purpose],sssPurposeCODE+sssPurposeRODATA+sssPurposeDATA ; Presence of LEDATA specifies purpose.
    .l2:MOV [%LastSegm],EAX
        XOR EAX,EAX
        JSt [%OmfStatus],pfomfRec32,.l3:
        LODSW
        JMP .l4:
    .l3:LODSD
    .l4:MOV [%DataOffs],EAX                          ; EAX is data offset within segment.
        SUB EDX,ESI                                  ; Negative delta in record position.
        ADD ECX,EDX                                  ; ESI,ECX is now record size minus segment index and data offset - netto data.
        MOV [%DataPtr],ESI
        MOV [%DataSize],ECX
        MOV EDX,ECX
        BufferRetrieve [EDI+SSS.EmitBuffer]
        ; ESI,ECX is now previous data contents, EAX is offset of new data in segment.
        ADD EDX,EAX                                  ; EDX is now the offset of end of new data within segment.
        SUB EDX,ECX                                  ; How many bytes needs the buffer be increased.
        JBE .l5:                                     ; If buffer is already large enough.
        PUSH EAX
          BufferNew [EDI+SSS.EmitBuffer],EDX         ; Increase the buffer.
          BufferRetrieve [EDI+SSS.EmitBuffer]
        POP EAX
    .l5:LEA EDI,[ESI+EAX]                            ; Position of new data in EmitBuffer.
        MOV [%DataOffs],EDI                          ; Save the pointer for later fixups.
        MOV ESI,[%DataPtr]
        MOV ECX,[%DataSize]
        REP MOVSB
        JMP .NextRecord:

    .LIDATA32:
        SetSt [%OmfStatus],pfomfRecLi32
        JMP .d1:
    .LIDATA:
        RstSt [%OmfStatus],pfomfRecLi32
    .d1:LEA EDX,[ESI+ECX]
        MOV [%LiDbEnd],EDX
        Invoke PfomfLoadSegment,[%SegdefBuf],ESI
        JC .E8533:
        MOV EDI,EAX                                  ; Segment which the data are emitted to.
        SetSt [EDI+SSS.Status],sssNotBSS
        JSt [EDI+SSS.Purpose],sssPurposeCODE|sssPurposeDATA|sssPurposeDRECTVE,.d2:
        SetSt [EDI+SSS.Purpose],sssPurposeCODE+sssPurposeDATA ; Presence of LEDATA specifies purpose.
    .d2:MOV [%LastSegm],EAX
        XOR EAX,EAX
        JSt [%OmfStatus],pfomfRecLi32,.d3:
        LODSW
        JMP .d4:
    .d3:LODSD                                        ; EAX is expanded data offset within segment.
    .d4:MOV [%DataOffs],EAX                          ; Save the pointer for later fixups.
        MOV [%LiDbStart],ESI
        ; Expansion of LIDATA datablock is postponed to .ExpandLIDATA
        ;   until all following FIXUPP records have been processed.
        BufferClear [%LiOutEmitBuf]                  ; Prepare expansion buffers for .ExpandLIDATA.
        BufferClear [%LiInpRelocBuf]
        BufferClear [%LiOutRelocBuf]
        SetSt [%OmfStatus],pfomfLiPending
        JMP .NextRecord:

    .FIXUPP32:
        SetSt [%OmfStatus],pfomfRec32
        JMP .NextSubrecord:
    .FIXUPP:
        RstSt [%OmfStatus],pfomfRec32
    .NextSubrecord:
        MOV EAX,[%ModPtr]                            ; The next record.
        DEC EAX                                      ; Omit checksum byte of the current record.
        CMP ESI,EAX
        JNB .NextRecord:                             ; If there are no more fixup/thread subrecords.
        XOR EAX,EAX
        LODSB                                        ; Subrecord 1st byte.
        TEST AL,1000_0000b
        JNZ .FixupSubrecord:

    .ThreadSubrecord:
        MOVZX ECX,AL
        LEA EDI,[%ThreadTab]
        AND CL,0000_0011b                            ; ECX is now the thread number 0..3.
        AND AL,1111_1100b                            ; Get rid of thread number.
        Dispatch AL,pfomfThF0,pfomfThF1,pfomfThF2,pfomfThF4,pfomfThF5,pfomfThT0,pfomfThT1,pfomfThT2,pfomfThT3,pfomfThT4,pfomfThT5,pfomfThT6,
        JMP .E8533:
    .pfomfThT4:
    .pfomfThT0:Invoke PfomfLoadSegment,[%SegdefBuf],ESI ; Target is specified by SEGDEF index.
        JMP .t1:
    .pfomfThT5:
    .pfomfThT1:Invoke PfomfLoadGroup,[%GrpdefBuf],ESI   ; Target is specified by GRPDEF index.
        JMP .t1:
    .pfomfThT6:
    .pfomfThT2:Invoke PfomfLoadExtern,[%ExtdefBuf],ESI  ; Target is specified by EXTDEF index.
        JMP .t1:
    .pfomfThT3:LODSW                                    ; Target is specified by explicit PARA segment address. Not supported.
        SUB EAX,EAX
    .t1:MOV [EDI+16+4*ECX],EAX                          ; Store target segment to the thread table
        JMP .NextSubrecord:
    .pfomfThF0:Invoke PfomfLoadSegment,[%SegdefBuf],ESI ; Frame is specified by SEGDEF index.
        JMP .h1:
    .pfomfThF1:Invoke PfomfLoadGroup,[%GrpdefBuf],ESI   ; Frame is specified by GRPDEF index.
        JMP .h1:
    .pfomfThF2:Invoke PfomfLoadExtern,[%ExtdefBuf],ESI  ; Frame is specified by EXTDEF index.
        JMP .h1:
    .pfomfThF4:MOV EAX,[%LastSegm]                      ; Frame is specified by LEDATA segment.
        JMP .h1:
    .pfomfThF5:XOR EAX,EAX                              ; Frame is identical with target.
    .h1:MOV [EDI+0+4*ECX],EAX                           ; Store frame segment to the thread table.
        JMP .NextSubrecord:

    .FixupSubrecord:
        LEA EDX,[%Reloc]
        Clear EDX,Size=SIZE#RELOC
        ; Specify relocation type [EDX+RELOC.Status].
        MOV EBX,relocAbsVA
        TEST AL,0100_0000b                              ; relocAbsVA or relocRel?
        JNZ .f1:                                        ; If segment-relative (absolute relocation).
        MOV EBX,relocRel
    .f1:MOVZX ECX,AL
        SHR ECX,2
        AND CL,0x0F                                     ; ECX is now Location type 0d..15d.
        AND AL,0x03                                     ; Two most significant bits of fixup position within LEDATA.
        SHL EAX,8
        LODSB                                           ; EAX is now 10bit position in previous LEDATA record.
        MOV [%LdataOffs],EAX
        Dispatch CL,1d,2d,3d,5d,9d,11d,13d              ; Field "Location" specifies relocation type.
        JMP .E8533:                                     ; Unsupported location value.
        ; Location handlers. CL=location 0..15, ESI=^FixData, EDX=^RELOC, EBX=relocRel/relocAbsVA.
    .2d:MOV EBX,relocPara+relocWidth16                  ; 16bit base selector.
        JMP .f2:
    .3d:MOV EBX,relocFar+relocWidth16                   ; 32bit far pointer.
        JMP .f2:
    .11d:MOV EBX,relocFar+relocWidth32                  ; 48bit far pointer.
        JMP .f2:
    .13d:
    .9d:OR EBX,relocWidth32                             ; 32bit offset.
        JMP .f2:
    .1d: ; Abs16 or Rel16.                              ; 16bit offset.
    .5d: ; Abs16 or Rel16, resolved at load time.
        OR EBX,relocWidth16
    .f2:MOV [EDX+RELOC.Status],EBX
        ; Specify RELOC members .Addend, .Section, .Symbol.
        LEA ECX,[%ThreadTab]
        Invoke PfomfLoadFixData,ESI,EDX,ECX,[%LastSegm],[%SegdefBuf],[%GrpdefBuf],[%ExtdefBuf],[%OmfStatus]
        JC .E8533:
        ; Specify relocation .Org (offset of relocated word/dword).
        JSt [%OmfStatus],pfomfLiPending,.f8:            ; LIDATA fixups are treated differently.
        ; LEDATA fixup. RELOC.Org is related to data in segment .EmitBuf. Reloc is stored to segment's .RelocBuf.
        PUSH ESI                                        ; Save pointer to the next FIXUPP subrecord.
           MOV EDI,[%LastSegm]                          ; Segment whose data are fixed up.
           MOV EAX,[%DataOffs]                          ; Pointer to the recent LEDATA data within the .EmitBuffer.
           BufferRetrieve [EDI+SSS.EmitBuffer]          ; ESI is now at the start of .EmitBuffer contents.
           SUB EAX,ESI
        POP ESI
        ADD EAX,[%LdataOffs]                            ; Add 10bit offset or relocation within previous LEDATA record.
        MOV [EDX+RELOC.OrgLow],EAX
        MOV [EDX+RELOC.Section],EDI
        BufferStore [EDI+SSS.RelocBuffer],EDX,SIZE#RELOC; Relocation EDX is complete now.
        JMP .NextSubrecord:
    .f8: ; LIDATA fixup. RELOC.Org is related to DataBlock. Relocs are accumulated in %LiRelocBuf.
        MOV EAX,[%LdataOffs]                            ; 0..1023 offset of fixup related to %LiDbStart.
        MOV [EDX+RELOC.OrgLow],EAX
        BufferStore [%LiInpRelocBuf],EDX,SIZE#RELOC     ; Relocation EDX is complete now.
        JMP .NextSubrecord:

    .MODEND32:
        SetSt [%OmfStatus],pfomfRec32
        JMP .m1:
    .MODEND:
        RstSt [%OmfStatus],pfomfRec32
    .m1:JSt [%OmfStatus],pfomfImport,.70:               ; If MODEND ends one of imported modules.
    .m4:CMP ECX,1                                       ; MODEND ends the linkable module.
        JNA .70:                                        ; If there is no entry in this program.
        LODSB                                           ; Modend type byte.
        TEST AL,0100_0000b
        JZ .70:                                         ; If there is no entry in this program.
        LEA EDX,[%Reloc]                                ; Room for the relocation of entry point.
        Clear EDX,Size=SIZE#RELOC
        JSt [%OmfStatus],pfomfRec32, .m5:
        SetSt [EDX+RELOC.Status],relocWidth16+relocAbsVA
        JMP .m6:
    .m5:SetSt [EDX+RELOC.Status],relocWidth32+relocAbsVA
    .m6:LEA ECX,[%ThreadTab]
        Invoke PfomfLoadFixData,ESI,EDX,ECX,[%LastSegm],[%SegdefBuf],[%GrpdefBuf],[%ExtdefBuf],[%OmfStatus]
        JC .E8533:
    ;   Entry point loaded from MODEND record to RELOC EDX will be converted to the source Entry= value.
        MOV ESI,[EDX+RELOC.Symbol]
        MOV ECX,[ESI+SYM.NameSize]
        MOV ESI,[ESI+SYM.NamePtr]
        LEA EAX,[ECX+12]                                ; Room for entry symbol name + addend.
        PoolNew [EDI+PGM.Pool],EAX,Align=Byte
        MOV [EDI+PGM.Pgmopt.EntryPtr],EAX
        PUSH EBX,EDI
         MOV EDI,EAX
         MOV EBX,EAX
         REP MOVSB                                       ; Symbol name, e.g. CODE.
         MOV EAX,[EDX+RELOC.AddendLow]
         TEST EAX
         JZ .m8:
         JS .m7:
         MOVB [EDI],'+'
         INC EDI
    .m7: StoD EDI,Signed=Yes,Size=12,Align=Left          ; Symbol offset, e.g. +256.
    .m8: MOV ECX,EDI
         SUB ECX,EBX
        POP EDI,EBX
        MOV [EDI+PGM.Pgmopt.EntrySize],ECX
    .70:LEA ESI,[%ModulePgm] ; ModulePgm is completely loaded, store it to BaseProgram.ModulePgmList.
        Invoke PfDrectveDestroy::,ESI                   ; An alternative way to specify EXPORT/IMPORT in OMF format using [DRECTVE] pseudosegment.
        Invoke PgmDetectImportModule::,ESI              ; Set pgmImpLibMember flag when it's a pure import module.
        MOV EBX,[%BaseProgram]
        JNSt [ESI+PGM.Status],pgmImpLibMember,.79:      ; If it is an ordinary object module (with emitted contents).
        ; The just loaded module is pure import library member. Import module will be created instead.
        ListGetFirst [ESI+PGM.SymList]
        JZ .79:
    .71:JSt [EAX+SYM.Status],symImport,.72:
        ListGetNext EAX
        JNZ .71:
        JMP .79:
    .72:Invoke PgmCreateImportModule::,EBX,[EAX+SYM.DllNamePtr],[EAX+SYM.DllNameSize], \
                                       [EAX+SYM.OrdinalNr],[EAX+SYM.Status],[EAX+SYM.NamePtr],[EAX+SYM.NameSize], \
                                       [EAX+SYM.InterNamePtr],[EAX+SYM.InterNameSize]
        JMP .80:

    .ExternSymbolAndSegmentCreate: PROC             ; Used in .EXTDEF: and .IMPDEF: handlers.
    ; Input: Unique name of nonexisting external symbol:
    ;    ESI,ECX=external symbol and segment name.
    ;    [%OmfStatus] flags pfomfImport and pfomfByOrdinal.
    ;    [%ModulePgm] Linked or imported program.
    ; Output:New symbol and segment are stored on .SymList and .SssList of [%ModulePgm].
    ;    EDX=pointer to the new symbol.
    ;    EDI=pointer to the new segment.
    ;    ESI,ECX unchanged.
    ;    EBX=Pointer to the linked/imported PGM at %ModulePgm.
    ;    EAX=?
         LEA EBX,[%ModulePgm]
         ListNew [EBX+PGM.SymList],Zeroed=yes
         MOV EDX,EAX                                 ; New symbol symSe on the list.
         ListNew [EBX+PGM.SssList],Zeroed=yes
         MOV EDI,EAX                                 ; New extern pseudosection sssExtern.
         MOV EAX,symExtern+'A'                       ; Loaded extern symbol status.
         JNSt [%OmfStatus],pfomfImport,.c5:          ; Skip when the loaded file is statically linked rather than imported.
         JSt  [%OmfStatus],pfomfByOrdinal,.c4:       ; If this IMPDEF specifies import by ordinal.
         MOV EAX,symExtern+symImport+'A'
         JMPS .c5:
     .c4:MOV EAX,symExtern+symImport+symImportedByOrd+'A'
     .c5:MOV [EDX+SYM.Status],EAX
         SetSt [EDI+SSS.Status],sssExtern+sssPublic
         JNSt [%OmfStatus],pfomfImport,.c6:
         SetSt [EDI+SSS.Purpose],sssPurposeIMPORT
     .c6:PoolStore [EBX+PGM.Pool],ESI,ECX,Align=BYTE ; Make symbol+section name nonvolatile.
         MOV [%InterNamePtr],EAX
         MOV [%InterNameSize],ECX
         MOV [EDX+SYM.NamePtr],EAX
         MOV [EDX+SYM.NameSize],ECX
         MOV [EDX+SYM.InterNamePtr],EAX
         MOV [EDX+SYM.InterNameSize],ECX
         MOV [EDX+SYM.Section],EDI
         MOV [EDI+SSS.NamePtr],EAX
         MOV [EDI+SSS.NameSize],ECX
         MOV [EDI+SSS.SymPtr],EDX                    ; Pointer to the symbol in imported program.SymList.
         MOV [EDI+SSS.SegmPtr],EDI                   ; Let extern pseudosegment.SegmPtr point to itself.
         RET
        ENDP .ExternSymbolAndSegmentCreate:

    .ExpandLIDATA: PROC ; Postponed expansion of DataBlock at [%LiDbStart]..[%LiDbEnd].
        ; Relocations are stored in [%LiRelocBuf], their .Org related to [%LiDbStart].
        Invoke PfomfLoadDataBlock,[%LiDbStart],[%LiDbStart],[%LiDbEnd],[%LiOutEmitBuf], \
                                  [%LiInpRelocBuf],[%LiOutRelocBuf],[%OmfStatus]
        ; Expanded data are returned in [%LiOutEmitBuf].
        ; Expanded relocations are returned in [%LiOutRelocBuf],
        ;  their .Org related to expanded data (starting from 0).
        MOV EDI,[%LastSegm]
        BufferRetrieve [%LiOutEmitBuf]
        BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
        Invoke RelocRelocInBuffer::,[%LiOutRelocBuf],[%DataOffs]
        BufferRetrieve [%LiOutRelocBuf]
        BufferStore [EDI+SSS.RelocBuffer],ESI,ECX
        RET
       ENDP .ExpandLIDATA:

    .79:ListStore [EBX+PGM.ModulePgmList],ESI
        MOV [%ReturnEAX],EAX
    .80:Invoke EaBufferRelease::,[%LiInpRelocBuf]
        Invoke EaBufferRelease::,[%LiOutRelocBuf]
        Invoke EaBufferRelease::,[%LiOutEmitBuf]
        Invoke EaBufferRelease::,[%ExtdefBuf]
        Invoke EaBufferRelease::,[%GrpdefBuf]
        Invoke EaBufferRelease::,[%SegdefBuf]
        Invoke EaBufferRelease::,[%LnamesBuf]
    .90:MOV EAX,[%ReturnEAX]
        TEST EAX                                        ; Set ZF=1 if aborted.
        EndProcedure PfomfLoadModule

      ENDPROGRAM pfomf

▲Back to the top▲