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.
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 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
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.
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
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.
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.
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 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 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
EuroAssembler does not create iterated LIDATA records.
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
relocWidth
flag in Reloc.Status
specifies if the
currently stored OMF FIXUPP record is 16bit or 32bit.
THREAD subrecords are not generated by EuroAssembler.
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 Procedure OutputStream, Pgm Invoke PfomfStoreModule, [%OutputStream],[%Pgm] EndProcedure 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 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 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 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 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 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 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
pfomfRecLi32
whether the RepeatCount is 16bit or 32bit.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
Pgm.ModulePgmList
and marked as pgmUsed
.
BasePgm.ModulePgmList
.
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
BaseProgram.ModulePgmList
. The module starts with THEADR or LHEADR record
and it terminates with MODEND record.BaseProgram.ModulePgmList
.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