EuroAssembler Index Manual Download Source Macros


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).


pfomf PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
 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.
Invoked by
PfomfStoreModule
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 PfomfStoreModule
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
     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
     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 PfomfStoreModule
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 LEDATA store.
DataEnd    LocalVar ; Pointer to   end of data in segment.EmitBuffer.
OffsPtr    LocalVar ; Offset corresponding to the 1st byte of current LEDATA record. Increased after 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 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]
     BufferRetrieve [EBX+SSS.EmitBuffer]
     JECXZ .90: ; If no data in segment.
     MOV [%DataPtr],ESI ; Start with 1st data byte.
     ADD ECX,ESI
     MOV [%DataEnd],ECX
     BufferRetrieve [EBX+SSS.RelocBuffer]
     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.
    ; BufferNew [%DataBuf],4
    ; MOV [EAX],EDX ; LEDATA32 offset.
     JMP .20:
 .15:BufferStoreWord [%DataBuf],EDX ; LEDATA offset.
   ;  BufferNew [%DataBuf],2
   ;  MOV [EAX],DX ; LEDATA offset.
 .20:; Compute maximal OffsEnd due to limited OMF record size {1024).
     MOV EAX,[%DataEnd]
     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 no more relocations for this segment.
     MOV EAX,[ESI+RELOC.OrgLow]
     CMP EAX,[%OffsPtr]
     JB .40: ; If RELOC doesn't belong to 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.
     ; Decide what width (16 or 32) FIXUPP should be used.
     MOV EAX,[%Fix16Buf]
     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 record is full.
 .40:ADD ESI,SIZE#RELOC  ; Try next relocation if fixup subrecord was successfully stored.
     MOV [%RelocPtr],ESI
     JMP .30: ; Try if the next RELOC correspondes with LEDATA, too.
 .50:; Relocation ESI could not be stored because FixBuf is full
     ; or because the relocated location spans over OffsEnd.
     ; The whole location will be omitted from LEDATA.
     MOV EAX,[ESI+RELOC.OrgLow]
     CMP EAX,[%OffsEnd]
     JAE .60: ; If already omitted.
     MOV [%OffsEnd],EAX
 .60:; OffsEnd is finalized, now its time to flush LEDATA and FIXUP records.
     MOV EDX,[%OffsEnd]
     SUB EDX,[%OffsPtr] ; EDX is now LEDATA data netto size.
     JNA .90: ; If 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: ; If no fixups for this LEDATA.
     Invoke PfomfStoreRecord,[%Stream],FIXUPP,[%Fix16Buf] ; Store FIXUPP16.
 .70:BufferRetrieve [%Fix32Buf]
     JECXZ .80:
     Invoke PfomfStoreRecord,[%Stream],FIXUPP32,[%Fix32Buf] ; Store FIXUPP32.
 .80:JMP .10: ; Go store 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 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 output BUFFER where the subrecord should be written to.
Reloc is pointer to an input RELOC object which gets transformed to fixup subrecord.
Offset is offset (0..1021) from of data payload in previous LEDATA record from the start of segment.
SegmPtr is pointer to segment SSS where the relocated object is.
Output
CF=0, fixup subrecord 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 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]
    ; EDX is now address 0..1023 relative to payload in previous LEDATA.
    TEST EDX,~0x3FF
    Msg cc=NZ,'9975',EDX,0x9C ; Internal error: record size !1D exceeded 1024 in OMF record type 0x!2H.
    JNZ .90: ; This should never happen.
    ; Construct 16bit FIXUP subrecord field Locat in DX.
    XCHG DH,DL ; Location is big endian.
    MOV EAX,[ESI+RELOC.Status]
    JSt EAX,relocResolved|relocDisp8,.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:
.W3731:Msg '3731',[%SegmPtr],[ESI+RELOC.OrgLow] ; Self-relative relocation to an absolute VA at [!1S]:!2Hh is not linkable.
    JMP .Err:
.W3732:Msg '3732',[%SegmPtr],[ESI+RELOC.OrgLow] ; Far relocation to an absolute VA at [!1S]:!2Hh is not linkable.',0
    JMP .Err:
.relocAbsVA:JSt [ESI+RELOC.Status],relocWidth32,.Abs32:
    OR DL,11_0001_00b ; Segment-relative, Location=1 (16bit offset).
    JMPS .10:
.Abs32:
    OR DL,11_1001_00b ; Segment-relative, Location=9 (32bit offset).
    JMPS .10:
.relocRel:JSt  [ESI+RELOC.Status],relocWidth32,.Rel32:
    OR DL,10_0001_00b ; IP-relative, Location=1 (16bit offset).
    JMPS .10:
.Rel32:
    OR DL,10_1001_00b ; IP-relative, Location=9 (32bit offset).
    JMPS .10:
.relocFar:JSt  [ESI+RELOC.Status],relocWidth32,.Far32:
    OR DL,11_0011_00b ; Segment-relative, Location=3 (segment:16bit offset).
    JMPS .10:
.Far32:
    OR DL,11_1011_00b ; Segment-relative, Location=11 (segment:32bit offset).
    JMPS .10:
.relocPara:
    OR DL,11_0010_00b ; Segment-relative, Location=2 (base segment).
.10:; Field Locat is now completed in DX. 
    ; Construct 8bit FIXUP subrecord field FixData in DL.
    MOV EDI,[ESI+RELOC.Target]  ; Target object (segment/group/extern).
    TEST EDI
    JNZ .15:
.13:JSt [ESI+RELOC.Status],relocRel,.W3731:
    JSt [ESI+RELOC.Status],relocFar,.W3732:
    JMP .F9974:
.15:MOV EDI,[EDI+SSS.SegmPtr]
    TEST EDI
    JZ .13:
    MOV EAX,[EDI+SSS.Status] ; EAX is now the type of target SSS object.
    MOV ECX,[ESI+RELOC.DispLow] ; ECX is additional displacement of relocated object (usually 0).
    JSt EAX,sssSegment,.sssSegment:
    JSt EAX,sssExtern,.sssExtern:
    JSt EAX,sssGroup,.sssGroup:
    JMP .F9974:
.sssExtern:
    BufferStoreWord [%FixBuffer],EDX ; Store field Locat.
    MOV DL,0101_0110b ; Frame is determined by Target EXTDEF.  F5,T6,no displacement.
    JECXZ .20:
    AND DL,1111_1011b ; Frame is determined by Target EXTDEF.  F5,T2,displacement present.
.20:CALL .StoreFixDat:
.StoreFixDat:PROC1 ; Store FixDat byte from DL to %FixBuffer.
             BufferStoreByte [%FixBuffer],EDX
             RET
             ENDP1 .StoreFixDat:
    Invoke PfomfStoreIndex,[%FixBuffer],[EDI+SSS.SegmIndex] ; TargetDatum EXTDEF.
    JMP .StoreTargetDisplacement:
.sssGroup:
    BufferStoreWord [%FixBuffer],EDX ; Store field Locat.
    MOV DL,0101_0101b ; Frame is determined by Target GRPDEF. F5,T5,no displacement.
    JECXZ .30:
    AND DL,1111_1011b ; Frame is determined by Target GRPDEF. F5,T1,displacement present.
.30:CALL .StoreFixDat:
    Invoke PfomfStoreIndex,[%FixBuffer],[EDI+SSS.SegmIndex] ; TargetDatum GRPDEF.
    JMP .StoreTargetDisplacement:
.sssSegment:
    BufferStoreWord [%FixBuffer],EDX ; Store field Locat.
    MOV EBX,[EDI+SSS.GroupPtr] ; Test if segment EDI is member of a group.
    TEST EBX
    JZ .50:
    ; Target segment EDI belongs to group EBX.
    MOV DL,0001_0100b ; Frame is determined by Target GRPDEF. F1,T4,no displacement.
    JECXZ .40:
    AND DL,1111_1011b ; Frame is determined by Target GRPDEF. F1,T0,displacement present.
.40:CALL .StoreFixDat:
    Invoke PfomfStoreIndex,[%FixBuffer],[EBX+SSS.SegmIndex] ; FrameDatum GRPDEF.
    JMP .70:
.50: ; Target segment EDI does not belong to a group.
    MOV DL,0101_0100b ; Frame is determined by Target SEGDEF. F5,T4,no displacement.
    JECXZ .60:
    AND DL,1111_1011b ; Frame is determined by Target SEGDEF. F5,T0,displacement present.
.60:CALL .StoreFixDat:
.70:Invoke PfomfStoreIndex,[%FixBuffer],[EDI+SSS.SegmIndex] ; TargetDatum SEGDEF.
.StoreTargetDisplacement: ; Store displacement ECX as 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.
  ; MOV EDX,2 ; Displacement size.
    JSt [ESI+RELOC.Status],relocWidth16,.80:
    BufferStoreDword [%FixBuffer],ECX
    JMP .90:
.80: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
t7010 t7013 t7016 t7019 t7022 t7025 t7028 t7031 t7037 t7043 t7049 t7094 t7097
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.
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.
Invoked from
PfOutput
Invokes
DictSearchByData EaBufferRelease EaBufferReserve ExpReportError PfomfStoreData PfomfStoreIndex PfomfStoreName PfomfStoreRecord PgmEvalEntry PgmOrderSegments RelocSort
Invoked by
PflibomfStoreModule PfomfCompile
PfomfStoreModule Procedure OutputStream, Pgm
RecBuf     LocalVar ; Buffer for the 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 RelocSort::,EDX
     Invoke PgmOrderSegments::,EDX
     ; Create OMF records.
     MOV EAX,LHEADR
     JSt [EDX+PGM.Status],pgmIsModule,.10:
     MOV EAX,THEADR
.10: CALL .THEADR:       ; Module name.
     CALL .COMENT_00:    ; Compiler info.
     ; If message "I0010 EuroAssembler version !1S started." is suppressed,
     ;  do not public 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::+EAOPT.NoWarn+0010/8],0x01<<(0010\8) ; Test if NOWARN=0010.                          ; >>
     JNZ .20: ; Skip if 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]
     ListGetFirst [EDX+PGM.SssList]
     JZ .70:
 .30:JNSt [EAX+SSS.Status],sssSegment,.50:
     JSt [EAX+SSS.Purpose],sssPurposeBSS,.50: ; Do not emit.
     JSt [EAX+SSS.Purpose],sssPurposeCODE|sssPurposeDATA,.40: ; Always emit.
     JNSt [EAX+SSS.Status],sssNotBSS,.50: ; Emit stack segment only if it contains initialized data.
 .40:Invoke PfomfStoreData,[%OutputStream],EAX ; LEDATA and FIXUPP records of segment EAX.
 .50:ListGetNext EAX
     JNZ .30: ; The next segment.
 .70:CALL .MODEND:       ; End of OMF file.
 .90:Invoke EaBufferRelease::,[%RecBuf]
     JMP .99:

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

.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 .THEADR:

.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 message "I0010 EuroAssembler version !1S started." is suppressed, skip further info in record.
     TESTB [Ea.Eaopt::+EAOPT.NoWarn+0010/8],0x01<<(0010\8)                                               ; >>
     JNZ .c1:
     BufferStoreByte [%RecBuf],' ' ; Otherwise append 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 .COMENT_00:

.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::+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+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]
     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 .COMENT_9D:

.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::+FILE.Name]
     BufferStorePascalString [%RecBuf],EAX ; Pascal-formated string with ASCIIZ filename.
     Msg cc=C,'8512',[ESI+4*EDI+FILE.Name] ; Length of linked file name "!1$" exceeds 254 characters.
     Invoke PfomfStoreRecord,[%OutputStream],0x88,[%RecBuf]
     ; Now store records of all included files.
     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.
     MOV EAX,[ESI+4*EDI] ; EAX=^FILE.
     LEA EAX,[EAX+FILE.Name]
     BufferStorePascalString [%RecBuf],EAX ; Pascal-formated string with filename.
     Msg cc=C,'8512',[ESI+4*EDI+FILE.Name] ; Length of linked file name "!1$" exceeds 254 characters.
     Invoke PfomfStoreRecord,[%OutputStream],COMENT,[%RecBuf]
     INC EDI
     JMP .c2:
 .c8:Invoke PfomfStoreRecord,[%OutputStream],COMENT,[%RecBuf] ; The last empty COMENT 0xE9 signalizes end of dependencies.
     RET
     ENDP .COMENT_E9:

.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"%EaDefaultDllName"
     MOV [EBX+SYM.DllNameSize],%EaDefaultDllNameSize
 .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.
     ; ImportedByName.
     Invoke PfomfStoreName,[%RecBuf],[EBX+SYM.NamePtr],[EBX+SYM.NameSize]
     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 .COMENT_A0_01:

.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 .COMENT_A0_02:


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

.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]
      ListGetFirst [EDX+PGM.SssList]
      JZ .ln9:
 .ln2: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.
      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:ListGetNext EBX
      JNZ .ln2:
 .ln9:Invoke PfomfStoreRecord,[%OutputStream],LNAMES,[%RecBuf] ; Flush %NameBuf to LNAMES record.
      RET
     ENDP .LNAMES:

.SEGDEF:PROC ; Segment definitions will be stored to SEGDEF records.
      MOVD [%SegIndex],0
      MOV EDX,[%Pgm]
      ListGetFirst [EDX+PGM.SssList]
      JZ .s99:
 .s10: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).                                                     >>
     ; MOV EDI,[EBX+SSS.Purpose]
      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:ListGetNext EBX
      JNZ .s10: ; The next segment.
 .s99:RET
     ENDP .SEGDEF:

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

.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:
     MOV ESI,[EBX+SYM.Section]
     ; Create EXTDEF record for the external pseudosegment ESI.
     MOV ECX,[%ExtIndex]
     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 .EXTDEF:

.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,.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.
     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 EDX,[ECX+SSS.GroupPtr]
     MOV ECX,[ECX+SSS.SegmIndex]
     TEST EDX
     JZ .p2:
     MOV EDI,[EDX+SSS.SegmIndex]
 .p2:Invoke PfomfStoreIndex,[%RecBuf],EDI
     Invoke PfomfStoreIndex,[%RecBuf],ECX
     OR ECX,EDI
     JNZ .p3: ; Skip base frame when base segment is defined.
     BufferNew [%RecBuf],2
     MOVW [EAX],0 ; Base frame.
 .p3:MOV EDI,[EBX+SYM.NamePtr]
     MOV EDX,[EBX+SYM.NameSize]
     Invoke PfomfStoreName,[%RecBuf],EDI,EDX
     BufferNew [%RecBuf],2
     MOV ECX,[EBX+SYM.OffsetLow]
     MOV [EAX],CX
     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 .PUBDEF16:

.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,.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 EDX,[ECX+SSS.GroupPtr]
     MOV ECX,[ECX+SSS.SegmIndex]
     TEST EDX
     JZ .p2:
     MOV EDI,[EDX+SSS.SegmIndex]
 .p2:Invoke PfomfStoreIndex,[%RecBuf],EDI
     Invoke PfomfStoreIndex,[%RecBuf],ECX
     OR ECX,EDI
     JNZ .p3: ; Skip base frame when base segment is defined.
     BufferNew [%RecBuf],2
     MOVW [EAX],0 ; Base frame.
 .p3:MOV EDI,[EBX+SYM.NamePtr]
     MOV EDX,[EBX+SYM.NameSize]
     Invoke PfomfStoreName,[%RecBuf],EDI,EDX
     BufferNew [%RecBuf],4
     MOV ECX,[EBX+SYM.OffsetLow]
     MOV [EAX],ECX
     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 .PUBDEF32:

.MODEND:PROC ; Module end marker, entry definition.
     MOV EDX,[%Pgm]
     MOV ECX,[EDX+PGM.Pgmopt+PGMOPT.EntrySize]
     JECXZ .NoEntry:
     Invoke PgmEvalEntry::,EDX
     LEA EDI,[EDX+PGM.EntryExp]
     LEA EAX,[EDX+PGM.Pgmopt+PGMOPT.EntryPtr] ; Prepare error Msg !1S argument.
     Invoke ExpReportError::,EDI
     JC .NoEntry: ; On error emit no entry.
     ; EDI is valid expression with program entry.
     LEA EBX,[EDI+EXP.Low] ; EBX is now pointer to displacement.
     MOV ECX,[EDI+EXP.Status]
     Dispatch CL,'A','N','F'
     Msg '7711',EAX ; Invalid program entry point "!1S".
.NoEntry:
     BufferClear [%RecBuf] ; Remove any data when ENTRY is wrong and should be aborted.
     BufferStoreByte [%RecBuf],0 ; Module type=0.
     JMP .e7:
 .F: ; Entry is specified as immediate far pointer, e.g. 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: 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 grouped segment.
     MOVB [EAX+1],0x10 ; FixDataF1,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+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:
     ; MODEND will padd the module to OWORD.
     BufferRetrieve [%RecBuf]
     StreamGetSize [%OutputStream]
     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:Invoke PfomfStoreRecord,[%OutputStream],MODEND,[%RecBuf]
     RET
     ENDP .MODEND:
.99:EndProcedure PfomfStoreModule
↑ 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 .Seg, .Frame, .Disp 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 empty RELOC object, allocated by the caller.
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 buffer with indexed pointers to SSS segment.
GrpdefBuffer is pointer to buffer with indexed pointers to SSS group.
ExtdefBuffer is pointer to 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 has specified members .Seg, .Frame, .Disp.
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
    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 segment referred 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.
.pfomfFxF5:XOR EAX,EAX ; Zero signalizes that frame is identical with target.
.20: XCHG EDI,EAX ; Move the loaded frame segment to EDI, restore FixData AL.
     JC .Error: ; If wrong index (PfomfLoad* failed).
.30: MOV [EDX+RELOC.Frame],EDI ; Frame is now specified.
     ; Find the target.
    TEST AL,0000_1000b
    JZ .40:
    ; T=1, target is specified by previous target thread.
    MOV CL,0000_0011b ; Targt field.
    AND CL,AL ; ECX is now target thread number 0..3.
    MOV EDI,[EBX+16+4*ECX] ; Pointer to 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 AL.
     JC .Error: ; If wrong index (PfomfLoad* failed).
.60: MOV [EDX+RELOC.Target],EDI ; Target segment is now specified. FixData is in AL.
     TEST AL,0000_0100b
     MOV EAX,0
     JNZ .80: ; If P=1, no displacement is present.
     ; Load the target displacement (word or dword).
     JSt [%Status32],1,.70:
     LODSW
     JMP .80:
.Error:
     STC
     JMP .90:
.70: LODSD
.80: MOV [EDX+RELOC.DispLow],EAX
     SUB EAX,EAX
     MOV [EDX+RELOC.DispHigh],EAX
.90: MOV [%ReturnESI],ESI
   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 exists.
 .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.
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
t7013 t7031 t7103 t7121 t7193 t7211 t7307 t7325 t7421 t7460 t7475 t7493 t7559
PfomfLoadPgm Procedure BasePgm, ObjBegin, ObjSize, FileNamePtr
    MOV ESI,[%ObjBegin]
    MOV ECX,[%ObjSize]
    LEA EDX,[ESI+ECX]
    Invoke PfomfLoadModule,[%BasePgm],ESI,ESI,EDX,[%FileNamePtr],pgmoptOMF
    JZ .90:
;   ; Unlike library modules, object files explicitly requested with
;   ; LINK "file.obj" will be always statically linked to BasePgm.
;   SetSt [EAX+PGM.Status],pgmSelected
.90:EndProcedure PfomfLoadPgm
↑ PfomfLoadModule BaseProgram, FileBegin, ModBegin, ModEnd, FileName$, 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
Module is loaded from file to PGM structure and stored on BaseProgram.ModulePgmList.
Error
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 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 recent LEDATA data within the EmitBuffer.
LastSegm         LocalVar ; Pointer to recent SSS segment of LEDATA.
NamePtr          LocalVar ; Pointer to recent symbol name.
NameSize         LocalVar ; Size of recent symbol name.
DllNamePtr       LocalVar ; Pointer to recent DLL name.
DllNameSize      LocalVar ; Size of recent DLL name.
InterNamePtr     LocalVar ; Pointer to recent internal import name.
InterNameSize    LocalVar ; Size of 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 (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
    Invoke PgmoptSetLinkProp::,[%Format]
    SetSt EAX,pgmoptLibMember
    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 it the file is invalid.
    ADD [%ModPtr],ECX ; Prepare %ModPtr for the next OMF record.
    JNSt [%OmfStatus],pfomfLiPending,.20:
    ; Previous LIDATA record needs expansion,
    JECXZ .10: ;  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.
   JECXZ .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: ; Abort linking due to error in file.
    MOV ESI,[%RecPtr]
    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 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 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:
    JECXZ .NextRecord:
.n2:PUSH ESI ; Pointer to 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 too short, ignore.
    LEA EDX,[%Sss]
    Clear EDX,Size=SIZE#SSS
   ; MOV EAX,[%SegIndex]
   ; INC EAX
   ; MOV [%SegIndex],EAX
   ; MOV [EDX+SSS.SegIndex],EAX
    LODSB ; 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 256B.
    JMP .s4:
.5: MOV EDI,4 ; DWORD.
    JMP .s4:
.6: MOV EDI,4K
   ;JMP .s4:
.s4:MOV [EDX+SSS.Alignment],EDI
    XOR EAX,EAX
    JSt [%OmfStatus],pfomfRec32,.s5:
    LODSW
    JMP .s6:
.s5:LODSD
.s6:MOV [EDX+SSS.TopLow],EAX
    Invoke PfomfLoadName,[%LnamesBuf],ESI,[%BaseProgram] ; Segment name index.
    MOV [EDX+SSS.NamePtr],EAX
    MOV [EDX+SSS.NameSize],ECX
    JECXZ .E8533: ; Name must not be empty.
    Invoke PfomfLoadName,[%LnamesBuf],ESI,[%BaseProgram] ; Class name 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
.s8:ListStore [EDI+PGM.SssList],EDX ; Save the segment to loaded/imported Pgm.
    MOV [EAX+SSS.SegmPtr],EAX
    BufferStoreDword [%SegdefBuf],EAX
    JMP .NextRecord:

.GRPDEF:
    LEA EDX,[%Sss]
    Clear EDX,Size=SIZE#SSS
  ;  MOV EAX,[%GrpIndex]
  ;  INC EAX
  ;  MOV [%GrpIndex],EAX
  ;  MOV [EDX+SSS.SegmIndex],EAX
    SetSt [EDX+SSS.Status],sssGroup+sssWidth16+sssPublic
    Invoke PfomfLoadName,[%LnamesBuf],ESI,[%BaseProgram] ; Group name index.
    MOV [EDX+SSS.NamePtr],EAX
    MOV [EDX+SSS.NameSize],ECX
    JECXZ .E8533: ; Name must not be empty.
    LEA EDI,[%ModulePgm]
    ListStore [EDI+PGM.SssList],EDX
    MOV [EAX+SSS.GroupPtr],EAX
    PUSH EAX
      MOV EAX,ESP
      BufferStore [%GrpdefBuf],EAX,4
    POP EBX ; Ptr to group on linked Pgm.SssList.
.g2:MOV ECX,[%ModPtr] ; End of this record.
    SUB ECX,3
    CMP ESI,ECX
    JA .NextRecord:
    LODSB
    CMP AL,0xFF
    JNE .g2:
    Invoke PfomfLoadSegment,[%SegdefBuf],ESI
    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 no more external definitions in EXTDEF.
    LODSB ; External name size.
    MOVZX ECX,AL ; ESI,ECX is now external symbol name or imported external symbol internal name.
    JECXZ .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 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 extern.
    POP EDI
.e9:LODSB ; Skip unused type index.
    TEST AL,0x80
    JZ .e1:
    LODSB
    JMP .EXTDEF: ; More extern definitions may follow.

.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 linked/imported PGM at %ModulePgm.
;    EAX=?
     LEA EBX,[%ModulePgm]
     ListNew [EBX+PGM.SymList],Zeroed=yes
     MOV EDX,EAX ; New symbol on the list.
     ListNew [EBX+PGM.SssList],Zeroed=yes
     MOV EDI,EAX ; New extern pseudosection.
     MOV EAX,symExtern+'A' ; Loaded extern symbol status.
     JNSt [%OmfStatus],pfomfImport,.c5: ; Skip when the loaded file is 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 on imported program.SymList.
     MOV [EDI+SSS.SegmPtr],EDI ; Let extern pseudosegment.SegmPtr point to itself.
     RET
    ENDP .ExternSymbolAndSegmentCreate:

.PUBDEF32:
    SetSt [%OmfStatus],pfomfRec32
    JMP .p0:
.PUBDEF:
    RstSt [%OmfStatus],pfomfRec32
.p0:CMP ECX,7
    JB .NextRecord: ; Record 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 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 record position.
    Invoke PfomfLoadSegment,[%SegdefBuf],ESI
    JC .E8533:
    MOV EDI,EAX ; Segment where the data are emitted to.
    SetSt [EDI+SSS.Status],sssNotBSS
    JSt [EDI+SSS.Purpose],sssPurposeCODE|sssPurposeDATA|sssPurposeDRECTVE,.l2:
    SetSt [EDI+SSS.Purpose],sssPurposeCODE+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 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 where 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:

.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:

.FIXUPP32:
    SetSt [%OmfStatus],pfomfRec32
    JMP .NextSubrecord:
.FIXUPP:
    RstSt [%OmfStatus],pfomfRec32
.NextSubrecord:
    MOV EAX,[%ModPtr] ; The next record.
    DEC EAX ; Omit checksum byte of current record.
    CMP ESI,EAX
    JNB .NextRecord: ; If 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 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 versus relocRel?
    JNZ .f1: ; If segment-relative (absolute relocation).
    MOV EBX,relocRel
.f1:;SetSt [EDX+RELOC.Status],EBX
    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 relocation members .Seg, .Frame, .Displ.
    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 recent LEDATA data within the .EmitBuffer.
       BufferRetrieve [EDI+SSS.EmitBuffer] ; ESI is now 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
    ; Relocation EDX is complete now.
     BufferStore [EDI+SSS.RelocBuffer],EDX,SIZE#RELOC
     JMP .NextSubrecord:
.f8: ; LIDATA fixup. RELOC.Org is related to DataBlock. Reloc is stored to %LiRelocBuf.
     MOV EAX,[%LdataOffs] ; 0..1023 offset of fixup related to %LiDbStart.
     MOV [EDX+RELOC.OrgLow],EAX
     BufferStore [%LiInpRelocBuf],EDX,SIZE#RELOC
     JMP .NextSubrecord:

.MODEND32:
    SetSt [%OmfStatus],pfomfRec32
    JMP .m1:
.MODEND:
    RstSt [%OmfStatus],pfomfRec32
.m1:JSt [%OmfStatus],pfomfImport,.m9:; If MODEND ends one of imported modules.
.m4:CMP ECX,1 ; MODEND ends the linkable module.
    JNA .m9: ; If there is no entry in this program.
    LODSB ; Modend type byte.
    TEST AL,0100_0000b
    JZ .m9: ; If there is no entry in this program.
    LEA EDX,[%Reloc]
    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:
    MOV ECX,[EDX+RELOC.Target]
    JECXZ .m7:
    BufferStore [ECX+SSS.RelocBuffer],EDX
.m7:LEA EDI,[EDI+PGM.EntryExp]
    MOV [EDI+EXP.Seg],ECX
    MOV EAX,[EDX+RELOC.DispLow]
    MOV EDX,[EDX+RELOC.DispHigh]
    MOV [EDI+EXP.Low],EAX
    MOV [EDI+EXP.High],EDX
    MOV EAX,'N'
    JECXZ .m8:
    MOV AL,'A'
.m8:MOV [EDI+EXP.Status],EAX
.m9:
.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.
    ; 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:
.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▲