Pgm represents a program defined with source text between PROGRAM/ENDPROGRAM statements. Product of Pgm assembly is binary/object/executable file.
In other assemblers it is often referred as module or assembly unit . EuroAssembler accepts more than one program in a source file.
Pgm has its own memory pool.
EUROASM NOWARN=2101 pgm PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules. INCLUDEHEAD \ ; Include headers of another modules used in this module. syswin.htm, \ chunk.htm, \ ctx.htm, \ dict.htm, \ ea.htm, \ eaopt.htm, \ exp.htm, \ lst.htm, \ msg.htm, \ pass.htm, \ pf.htm, \ pgmopt.htm, \ reloc.htm, \ src.htm, \ sss.htm, \ stm.htm, \ sym.htm, \ var.htm, \ ;;
pgm HEAD ; Start of module interface.
PGM STRUC .NamePtr D DWORD ; Pointer to the program name. .NameSize D DWORD ; Size of the program name. .Pool D DWORD ; Program pool of dynamic memory. .Status D DWORD ; Program Boolean flags in PgmEncoding. ; +10h. .PassNr D DWORD ; Pass number. .CurrentSect D DWORD ; ^SSS with .Status:sssSection - a section which Pgm emits to. .CurrentStm D DWORD ; ^STM with the last parsed statement. .LinePtr D DWORD ; Pointer to memory-mapped physical line with current statement of this PROGRAM. ; +20h. .Errorlevel D DWORD ; Errorlevel of this program. .PassPtr D DWORD ; Pointer to PASS or 0 when no pass is initialized. .NrOfInitSegments D DWORD ; How many groups+segments in image are initialized (trailing BSS and STACK do not count). .ModulePgmList D DWORD ; ^LIST of PGM modules loaded from linked/imported files. ; +30h. .SssList D DWORD ; ^LIST with groups, segments, sections, externs and structures (SSS objects). .SymList D DWORD ; ^LIST with program symbols (SYM objects). .SegOrdBuffer D DWORD ; ^BUFFER with DWORD pointers to segments (^SSS) in image order. .SymOrdBuffer D DWORD ; ^BUFFER with DWORD pointers to symbols (^SYM) in image order. ; +40h. ; Synchronous tables describing files included in the program. .InclFilesNr D DWORD ; Number of DWORDS in .InclFilesTable,.InclLinePtrTable,.InclFileTimeTable. .InclFilesTable D DWORD ; Pointer to the table of DWORDs, each contains pointer to FILE structure. .InclLinePtrTable D DWORD ; Pointer to the table of DWORDs, each contains LinePtr to INCLUDE* statement. .InclFileTimeTable D DWORD ; Pointer to the table of DWORDs, each contains DosDateTime of included file. ; +50h. ; Synchronous tables describing files linked in the program. .MaxLinks D DWORD ; Copy of EUROASM MAXLINKS= option which was in charge on PgmCreateProgram. .LinkFilesNr D DWORD ; Number of DWORDS in .LinkFileNamesTable and .LinkLinePtrTable. .LinkFileNamesTable D DWORD ; Pointer to the table of DWORDs, each contains pointer to ASCIIZ filename. .LinkLinePtrTable D DWORD ; Pointer to the table of DWORDs, each contains LinePtr to LINK statement. ; +60h. .DSONameBuffer D DWORD ; ^BUFFER with DWORD pointer+size of the name of dynamically loaded DSO module without path. Empty if it isn't ELFSO format. .EntryExp DS EXP ; Program entry evaluated to EXP structure by PgmEvalEntry. .Pgmopt DS PGMOPT ; Program options of this program as PGMOPT object. .Eaopt DS EAOPT ; EUROASM options valid at the start of this program as EAOPT object. ENDSTRUC PGM
; Assembly flags. pgmEnvelope = 0x0000_0001 ; Artificial PROGRAM block encapsulating all source file statements. pgmEnvelDirty = 0x0000_0002 ; Something was emitted to envelope program, do list its PROGRAM/ENDPROGRAM statements. ; = 0x0000_0004 pgmNoMsg = 0x0000_0008 ; Used to suppress Msg during ChunkSubHead detection. pgmLastPass = 0x0000_0010 ; Program is in the final pass. pgmLastJustSet = 0x0000_0020 ; Set together with pgmLastPass, reset in SrcAssemble when used. pgmPassInit = 0x0000_0040 ; Pass was already initialized with 256/0x7C00 dummy bytes in COM/BOOT format. pgmFixingPass = 0x0000_0080 ; Pass number last but one (MAXPASSES-1). ; Linker flags. pgmLinking = 0x0000_0100 ; Program was finally assembled, now it is linked. pgmLoadsELFSO = 0x0000_0800 ; This base program loads at least one ELFSO library. pgmFormatELF = 0x0000_1000 ; Program format=ELF. pgmFormatELFX = 0x0000_2000 ; Program format=ELFX. pgmFormatELFSO = 0x0000_4000 ; Program format=ELFSO. pgmEntryReported = 0x0000_8000 ; E7710 or E7711 was already reported, see PgmGetEntryAddress. pgmSelected = 0x0001_0000 ; Module is referenced, it will be combined and linked to base program. pgmImpLibMember = 0x0002_0000 ; Module is a pure import library member. pgmIsModule = 0x0004_0000 ; This program is OMF library module. It requires 512 alignment. pgmRealMode = 0x0008_0000 ; Program format uses by default real-mode segment names [CODE]... rather than prot-mode names [.text]...
ENDHEAD pgm [.text]
PgmCreateProgram Procedure StmPtr MOV EBX,[%StmPtr] PoolNew [Src.Pool::],SIZE# PGM MOV EDI,EAX ; ^PGM. MOV [%ReturnEAX],EAX ; Initialize program object members. Clear EDI,Size=SIZE#PGM MOV EAX,[EBX+STM.ChunkPtr] MOV ECX,'0470' ; Assembling program "!1S". JNSt [EAX+CHUNK.Status],chunkEnvelope,.10: MOV ECX,'0270' ; Assembling source "!1S". SetSt [EDI+PGM.Status],pgmEnvelope .10:Invoke CtxPeek::,ctxPROGRAM,0 MsgUnexpected cc=C Msg ECX,EAX ; Context Assembling source/program "!1S". MOV ESI,[EAX+CTX.NamePtr] MOV ECX,[EAX+CTX.NameSize] MOV [EDI+PGM.NamePtr],ESI MOV [EDI+PGM.NameSize],ECX PoolCreate Size=%EaPoolSize, ErrorHandler=EaMallocError:: MOV [EDI+PGM.Pool],EAX MOV EDX,EAX MOV [EDI+PGM.CurrentStm],EBX MOV EAX,[EBX+STM.LinePtr] MOV [EDI+PGM.LinePtr],EAX ListCreate EDX,SIZE#SYM MOV [EDI+PGM.SymList],EAX ListCreate EDX,SIZE#PGM MOV [EDI+PGM.ModulePgmList],EAX PoolNew EDX,SIZE#PASS MOV [EDI+PGM.PassPtr],EAX ListCreate EDX,SIZE#SSS MOV [EDI+PGM.SssList],EAX BufferCreate [EDI+PGM.Pool],Size=16*4 ; Initialize for estimated 16 segments in the program. MOV [EDI+PGM.SegOrdBuffer],EAX BufferCreate [EDI+PGM.Pool],Size=2048*4 ; Initialize for estimated 2048 symbols in the program. MOV [EDI+PGM.SymOrdBuffer],EAX ; Default segments on SssList will be created later, when PROGRAM keyword will have been assembled to Pgmopt. LEA ESI,[EDI+PGM.Eaopt] CopyTo ESI, Ea.Eaopt::, Size=SIZE#EAOPT LEA ESI,[EDI+PGM.Pgmopt] CopyTo ESI, Src.Pgmopt::, Size=SIZE#PGMOPT ; Initialize Pgmopt from source Pgmopt. ; Allocate included files tables Pgm.InclFilesTable,Pgm.InclFileTimeTable,Pgm.InclLinePtrTable. MOV ECX,[Ea.Eaopt.MaxInclusions::] INC ECX SHL ECX,2 PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.InclFilesTable],EAX PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.InclFileTimeTable],EAX PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.InclLinePtrTable],EAX ; Allocate linked files tables Pgm.LinkFileNamesTable, Pgm.LinkLinePtrTable. MOV ECX,[Ea.Eaopt.MaxLinks::] MOV [EDI+PGM.MaxLinks],ECX INC ECX SHL ECX,2 PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.LinkFileNamesTable],EAX PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.LinkLinePtrTable],EAX ; Start with program pass number 1. INCD [EDI+PGM.PassNr] EndProcedure PgmCreateProgram
PgmDestroy Procedure PgmPtr, StmPtr MOV EBX,[%PgmPtr] JNSt [EBX+PGM.Status],pgmEnvelope,.50: ; Curent program is source envelope, Pgm.Errorlevel will be combined with Src.Errorlevel. MOV EAX,[Src.Errorlevel::] CMP EAX,[EBX+PGM.Errorlevel] JNA .30: MOV [EBX+PGM.Errorlevel],EAX .30: Invoke ChunkTotalLines:: ; Count lines in source+includes. MOV EDX,EAX Invoke PgmCheckDirty,EBX ; Returns CF=1 if something was emitted or linked to the envelope program. SBB EDI,EDI ; EDI is now -1 if program dirty, 0 if empty. JZ .40: ; If ending envelope program is empty, output file is not generated. Invoke PfOutput::,EBX ; Format and write envelope program to disk, possibly overwriting embedded program with the same name. .40: Msg '0750',EBX,EDX,[EBX+PGM.PassNr],[EBX+PGM.Errorlevel] ; Source program "!1S" (!2D lines) assembled in !3D passes with errorlevel !4D. TEST EDI JZ .80: ; Empty envelope program does not list maps and globals. JMPS .60: .50: ; Program EBX is not an envelope, i.e. it was explicitly specified as PROGRAM/ENDPROGRAM block. Invoke PfOutput::,EBX ; Generate output file (even when empty). Msg '0650',EBX,[EBX+PGM.PassNr],[EBX+PGM.Errorlevel] ; Program "!1S" assembled in !2D passes with errorlevel !3D. .60: ; Common continuation for both envelope and embedded program. JNSt [EBX+PGM.Pgmopt.Status],pgmoptLISTMAP,.70: Invoke PgmListMap,EBX,[%StmPtr] ; List map of linked groups and segments. .70: JNSt [EBX+PGM.Pgmopt.Status],pgmoptLISTGLOBALS,.80: Invoke PgmListGlobals,EBX,[%StmPtr] ; List global symbols. .80: MOV EDX,[EBX+PGM.Pool] PoolDestroy EDX Msg cc=C,'2575','Pgm',EDX ; Deallocation of virtual memory !1C.Pool !2Hh failed. .90:EndProcedure PgmDestroy
This procedure decides which symbols should be stored in the symbol table of COFF|ELF module.
It will put those symbols in desired order, sort them alphabetically, make them unique and store pointers
to those symbols into Pgm.SymOrdBuffer
.
Symbols which go to the symbol table:
.file
specifying the source file name.Symbols within each of those divisions are sorted alphabetically by name (FQN) and made unique.
SYM.NameIndex and SYM.NameDynIndex are updated.
PGM.SymList
specifies all program symbols.Pgm.SymOrdBuffer
is filled with DWORD pointers to
SYM symbols in the desired order.PgmOrderSymbols Procedure Pgm BufEnd LocalVar ; Pointer to the end of buffer contents. SymNulBuf LocalVar ; ^BUFFER for null and file symbols. SymSegBuf LocalVar ; Segment symbols. SymLocBuf LocalVar ; Local symbols. SymGlbBuf LocalVar ; Global symbols. Invoke EaBufferReserve::,PgmOrderSymbols MOV [%SymNulBuf],EAX Invoke EaBufferReserve::,PgmOrderSymbols MOV [%SymSegBuf],EAX Invoke EaBufferReserve::,PgmOrderSymbols MOV [%SymLocBuf],EAX Invoke EaBufferReserve::,PgmOrderSymbols MOV [%SymGlbBuf],EAX MOV EBX,[%Pgm] MOV EDX,[EBX+PGM.SymOrdBuffer] TEST EDX JNZ .05: BufferCreate [EBX+PGM.Pool] MOV [EBX+PGM.SymOrdBuffer],EAX .05:ListGetFirst [EBX+PGM.SymList] MOV EBX,[EBX+PGM.SymOrdBuffer] ; EBX is now the output buffer. JZ .90: .10:MOV EDX,[%SymNulBuf] CMPD [EAX+SYM.NameSize],0 JE .70: ; Store NUL and .file symbols. MOV EDX,[%SymSegBuf] JSt [EAX+SYM.Status],symSe,.70: ; Store segment's symbol. .30:MOV EDX,[%SymLocBuf] ; Symbol EAX is global or local. JNSt [EAX+SYM.Status],symScopeMask,.70: ; Go to store local symbols. JSt [EAX+SYM.Status],symResolved,.80: ; Omit already resolved extern symbols. MOV EDX,[%SymGlbBuf] ; Store public|extern|import|export symbol. .70:BufferStoreDword EDX,EAX .80:ListGetNext EAX ; The next program symbol. JNZ .10: PgmOrderSymbols.Reorder: PROC1 ; Copy pointers from buffer EDX to buffer EBX, remove duplicates. Clobbers EAX,ECX,EDX,ESI,EDI. Invoke EaBufferSort::,EDX ; Sort symbols in buffer EDX by name alphabetically. BufferRetrieve EDX ADD ECX,ESI MOV [%BufEnd],ECX JECXZ .R9: MOV EDI,-1 MOV EDX,EDI ; EDI,EDX will be the memory of previous symbol name. .R1:CMP ESI,[%BufEnd] JNB .R9: LODSD Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],EDI,EDX JE .R1: ; Skip duplicated symbol. MOV EDI,[EAX+SYM.NamePtr] ; Prepare for the next Compare. MOV EDX,[EAX+SYM.NameSize] BufferStoreDword EBX,EAX JMP .R1: .R9:RET ENDP1 PgmOrderSymbols.Reorder: .90:MOV EDX,[%SymNulBuf] CALL .Reorder: MOV EDX,[%SymSegBuf] CALL .Reorder: MOV EDX,[%SymLocBuf] CALL .Reorder: BufferRetrieve EBX SHR ECX,2 MOV [%ReturnECX],ECX MOV EDX,[%SymGlbBuf] CALL .Reorder: Invoke EaBufferRelease::,[%SymNulBuf] Invoke EaBufferRelease::,[%SymSegBuf] Invoke EaBufferRelease::,[%SymLocBuf] Invoke EaBufferRelease::,[%SymGlbBuf] EndProcedure PgmOrderSymbols
PgmGetCurrent Procedure SUB EAX,EAX MOV [%ReturnEAX],EAX .20: Invoke CtxPeek::, ctxPROGRAM,EAX JC .90: MOV ECX,[EAX+CTX.ObjPtr] JECXZ .20: MOV [%ReturnEAX],ECX .90:EndProcedure PgmGetCurrent
LISTLITERALS=ENABLED
.PgmListLiterals Procedure Pgm LSFound LocalVar ; Boolean flag. Nonzero if subproc.ListSection
found the literal section. SnBuffer LocalVar ; Temporary buffer to keep pointers to literals before sorting. @RTnum LocalVar ; Ordinal of runtime literal section (1,2,3..). Stm LocalVar Size=SIZE#STM ; Temprary fake statement used to feed StmListing. SssName LocalVar Size=8 ; Room for the literal section name, such as "@LT4" LEA EDI,[%SssName] MOV EAX,"@LT#" STOSD XOR EAX,EAX STOSD MOV [%@RTnum],EAX Invoke EaBufferReserve::,%^PROC MOV [%SnBuffer],EAX PUSHD [Src.Lst.Status::] ; Save the global listing status during PgmListLiteral's performance. LEA EBX,[%Stm] Invoke StmCreate::,EBX ; List data literals. MOV EAX,"LT64" CALL .ListSection: MOV EAX,"LT32" CALL .ListSection: MOV EAX,"LT16" CALL .ListSection: MOV EAX,"LT8" CALL .ListSection: MOV EAX,"LT4" CALL .ListSection: MOV EAX,"LT2" CALL .ListSection: MOV EAX,"LT1" CALL .ListSection: ; List code literals. MOV EAX,"RT0" CALL .ListSection: MOVD [%SssName],"@RT1" .50: MOV EAX,[%@RTnum] INC EAX MOV [%@RTnum],EAX LEA EDI,[%SssName+3] StoD EDI,Size=2,Signed=no CMP AL,9 MOV EAX,[%SssName+1] JA .60: AND EAX,0x00FF_FFFF .60: CALL .ListSection: CMPD [%LSFound],0 JNZ .50: ; If @RT section was found, try the next one. Invoke StmDestroy::,EBX ; Otherwise all code-literals were listed. POPD [Src.Lst.Status::] Invoke EaBufferRelease::,[%SnBuffer] .ListSection: PROC1 ; List literals from one section. ; Input: EAX=section name without '@', e.g. "LT32" or "RT0". ; EBX=Pointer to %Stm. ; Output:EBX,EBP unchanged, other register clobbered. MOVD [%LSFound],0 LEA EDI,[%SssName] MOV ECX,4 ; Section name size. MOV [EDI+1],EAX TEST EAX,0xFF00_0000 ; Test if section name has 4 or 5 characters. JZ .S1: INC ECX .S1:Invoke SssFindByName::,sssSection,0,EDI,ECX,0 JC .S9: JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.S9: MOV EDI,EAX ; A literal section was found. MOV [%LSFound],EAX BufferClear [%SnBuffer] MOV EDX,[%Pgm] MOV [EDX+PGM.CurrentSect],EDI ListGetFirst [EDX+PGM.SymList] ; Find all literal symbols which belong to section EDI. JZ .S4: .S2:; EAX is now pointer to a symbol from program's symbol list. JNSt [EAX+SYM.Status],symLiteral,.S3: ; Skip when it's not a literal symbol. CMP [EAX+SYM.Section],EDI JNE .S3: ; Skip when the symbol EAX does not belong to the listed section EDI. BufferStoreDword [%SnBuffer],EAX .S3:ListGetNext EAX JNZ .S2: ; The next symbol. .S4:BufferRetrieve [%SnBuffer] SAR ECX,2 ; ECX is now number of literal symbols. JZ .S9: ; Symbols whose pointers are now in array at ESI will be sorted by offset and then listed. ; Otherwise in recursive literals, such as=W =W value
, the inner literal with higher offset ; would be listed sooner, which looks weird. ShellSort ESI,ECX, 4, .ByOffset: .ByOffset: PROC1 ; Callback sorting procedure. PUSH EBX ; EBX,ECX are callee-save registers. MOV EBX,[ESI] MOV EDX,[EDI] MOV EAX,[EDX+SYM.OffsetHigh] CMP EAX,[EBX+SYM.OffsetHigh] JNE .B5: MOV EAX,[EDX+SYM.OffsetLow] CMP EAX,[EBX+SYM.OffsetLow] .B5: JNC .B9: ; Skip when they are in (desired) ascending order. MOV [ESI],EDX ; Otherwise swap both elements and return CF=1. MOV [EDI],EBX .B9:POP EBX RET ENDP1 .ByOffset: ; ESI is now an sorted array of ECX pointers to symbols which will be dumped as literal. .S5:LODSD PUSH ECX,ESI MOV EDX,EAX ; EDX is now ^SYM. SetSt [Src.Lst.Status::],lstLiteral ; Tell StmListing how to treat this object. RstSt [Src.Lst.Status::],lstNoData+lstTRUE+lstFALSE Invoke StmClean::,EBX MOV EAX,[%Pgm] MOV [EBX+STM.Program],EAX MOV [EAX+PGM.CurrentSect],EDI MOV [EBX+STM.Section],EDI BufferStore [EBX+STM.SrcBuffer],[EDX+SYM.NamePtr],[EDX+SYM.NameSize] BufferStore [EBX+STM.SrcBuffer],=W(0x0A0D),2 ; Make new line. MOV EAX,[EDX+SYM.Size] MOV [EBX+STM.Size],EAX MOV EAX,[EDX+SYM.OffsetLow] MOV EDX,[EDX+SYM.OffsetHigh] MOV [EBX+STM.OffsetLow],EAX MOV [EBX+STM.OffsetHigh],EDX MOV [EDI+SSS.OrgLow],EAX ; Prepare emitted data of this literal to STM.EmitBuffer. SUB EAX,[EDI+SSS.BottomLow] BufferRetrieve [EDI+SSS.EmitBuffer] ADD ESI,EAX MOV ECX,[EBX+STM.Size] BufferStore [EBX+STM.EmitBuffer],ESI,ECX ; Prepare relocations of this literal to STM.RelocBuffer. BufferRetrieve [EDI+SSS.RelocBuffer] JECXZ .S7: .S6: BufferStore [EBX+STM.RelocBuffer],ESI,SIZE#RELOC ADD ESI,SIZE#RELOC SUB ECX,SIZE#RELOC JA .S6: ; The next relocation. .S7: Invoke StmListing::,EBX .S8:POP ESI,ECX DEC ECX JNZ .S5: ; The next literal symbol. .S9:RET ENDP1 .ListSection: EndProcedure PgmListLiterals
PROGRAM
pseudoinstruction.PgmParameters Procedure PgmPtr, StmPtr MOV EBX,[%StmPtr] BufferRetrieve [EBX+STM.KeyBuffer] LEA EDX,[ESI+ECX] .40:CMP ESI,EDX JNB .90: ; If no more keywords. MOV ECX,ESI LODSD ; Pointer to keyword name, e.g.MODEL=
. MOV EDI,EAX LODSD ; Keyword name size. Invoke DictLookup::, DictPgmopt::, EDI,EAX Msg cc=C,'7821',ECX,DictPgmopt:: ; Unknown PROGRAM keyword "!1S". Expected one of !2L. MOV ECX,EAX ; Pgmopt index. LODSD ; Pointer to keyword value, e.g.SMALL
. MOV EDI,EAX LODSD ; Keyword value size. JC .40: ; Skip when the option was unknown {E7821). StripSpaces EDI,EAX MOV EBX,[%PgmPtr] %ord %SETA 0 opt %FOR %PgmoptList %ord %SETA %ord+1 %IF "%opt" == "ENTRY" %EXITFOR opt %ENDIF %ENDFOR opt CMP ECX,%ord << 16 + varTypeSysPgmopt ; is the parameter ENTRY? >> JNE .70: Invoke PgmGlobalEntry,EBX,EDI,EAX .70:LEA EBX,[EBX+PGM.Pgmopt] Invoke PgmoptAssemble::, EBX,ECX,EDI,EAX JMP .40: .90:EndProcedure PgmParameters
ENTRY=
specifies an identifier and declares a global symbol from it.PgmGlobalEntry Procedure Program, EntryPtr, EntrySize TempStm LocalVar Size=SIZE# STM ClearLocalVar LEA EDI,[%TempStm] MOV EBX,[%Program] MOV ESI,[%EntryPtr] MOV ECX,[%EntrySize] MOV [EDI+STM.Program],EBX LEA EDX,[ESI+ECX] Invoke ExpParseIdentifier::,ESI,EDX,expFullstop JC .90: Invoke SymCreate::,symReferenced+symGlobalRef+symEntry,ESI,EAX,EDI .90: EndProcedure PgmGlobalEntry
PROGRAM LISTMAP=ON
from PgmDestroy after segments have been combined, ordered, linked and streamed by
PfOutput.
PgmListMap Procedure Pgm, StmPtr MsgBuffer LocalVar ; Local copy of statement MsgBuffer. Number LocalVar Size=20 MOV ESI,[%StmPtr] MOV EBX,[%Pgm] MOV EAX,[ESI+STM.MsgBuffer] MOV [%MsgBuffer],EAX JNSt [EBX+PGM.Status],pgmEnvelope,.10: ; Envelope program is listed only if something was emitted to it. JNSt [EBX+PGM.Status],pgmEnvelDirty,.90: .10: BufferStore$ [%MsgBuffer],=B'| **** ListMap "' BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt.OutFilePtr],[EBX+PGM.Pgmopt.OutFileSize] BufferStore$ [ESI+STM.MsgBuffer],=B'",model=' MOV EAX,pgmoptModelMask AND EAX,[EBX+PGM.Pgmopt.Status] MOV EDX,ESI Invoke DictSearchByData::,DictProgramModels::,EAX ; Returns ESI=^DICT. XCHG EDX,ESI BufferStore [ESI+STM.MsgBuffer],[EDX+DICT.Ptr],[EDX+DICT.Size] BufferStore$ [ESI+STM.MsgBuffer],=B',groups=' ; Count linked groups and segments. SUB EDI,EDI ; Group counter. SUB EDX,EDX ; Segment counter. BufferRetrieve [EBX+PGM.SegOrdBuffer] SHR ECX,2 JZ .22: .13: LODSD JNSt [EAX+SSS.Status],sssGroup,.16: INC EDI .16: JNSt [EAX+SSS.Status],sssSegment,.19: INC EDX .19: LOOP .13: .22: MOV EAX,EDI ; Number of groups in image. LEA EDI,[%Number] MOV ECX,EDI StoD EDI SUB EDI,ECX ; ECX,EDI is now decimal number of groups. MOV ESI,[%StmPtr] BufferStore [%MsgBuffer],ECX,EDI BufferStore$ [%MsgBuffer],=B',segments=' MOV EAX,EDX ; Number of segments in image. LEA EDI,[%Number] MOV ECX,EDI StoD EDI SUB EDI,ECX ; ECX,EDI is now decimal number of segments. BufferStore [%MsgBuffer],ECX,EDI BufferStore$ [%MsgBuffer],=B',entry=' ; List entry=. CMPD [EBX+PGM.Pgmopt.EntrySize],0 JNZ .32: ; Use %^ENTRY if not empty. BufferRetrieve [EBX+PGM.SegOrdBuffer] SHR ECX,2 JZ .29: .25: LODSD JSt [EAX+SSS.Purpose],sssPurposeCODE,.27: LOOP .25: .27: MOV ESI,[EAX+SSS.NamePtr] MOV ECX,[EAX+SSS.NameSize] .29: MOV EAX,[EBX+PGM.Pgmopt.Status] AND AL,pgmoptFormatMask CMP AL,pgmoptCOM JNE .32: BufferStoreByte [%MsgBuffer],'[' BufferStore [%MsgBuffer],ESI,ECX BufferStoreWord [%MsgBuffer],']:' BufferStore$ [%MsgBuffer],=B'00000100h' JMPS .34: .32: BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt.EntryPtr],[EBX+PGM.Pgmopt.EntrySize] .34: BufferStore$ [%MsgBuffer],=B',stack=' ; List stack=. MOV EAX,[%Pgm] ; Find the last STACK group|segment to EDX. XOR EDX,EDX BufferRetrieve [EAX+PGM.SegOrdBuffer] SHR ECX,2 JECXZ .49: .37: LODSD JNSt [EAX+SSS.Purpose],sssPurposeSTACK,.40: MOV EDX,EAX JSt [EDX+SSS.Status],sssGroup,.42: ; Prefer the stack group. .40: LOOP .37: TEST EDX ; Either 1st stack group or the last stack segment. JZ .49: ; Display nothing when no stack. MOV EDI,EDX ; ^SSS stack. .42: BufferStoreByte [%MsgBuffer],'[' BufferStore [%MsgBuffer],[EDX+SSS.NamePtr],[EDX+SSS.NameSize] BufferStoreWord [%MsgBuffer],']:' MOV EAX,[EBX+PGM.Pgmopt.Status] AND EAX,pgmoptFormatMask PUSH .Dispatched: Dispatch AL,%PfPgmoptList .pgmoptRSRC: .pgmoptPE: .pgmoptDLL: .pgmoptCOFF: .pgmoptLIBCOF: .pgmoptELF: .pgmoptELFX: .pgmoptELFSO: MOV EAX,[EBX+PGM.Pgmopt.SizeOfStackCommitLow] MOV EDX,[EBX+PGM.Pgmopt.SizeOfStackCommitHigh] RET .pgmoptLIBOMF: .pgmoptOMF: .pgmoptMZ: ; Stack pointer SP equals the size of the last stack segment. MOV EDI,EDX MOV EAX,[EDI+SSS.TopLow] MOV EDX,[EDI+SSS.TopHigh] SUB EAX,[EDI+SSS.BottomLow] SBB EDX,[EDI+SSS.BottomHigh] RET .pgmoptBIN: .pgmoptBOOT: .pgmoptCOM: ; Stack pointer is fixed to the end of [COM] segment. MOV EAX,0xFFFE SUB EDX,EDX RET .Dispatched: ; EDX:EAX=stack size. LEA EDI,[%Number] MOV ECX,EDI TEST EDX JZ .46: XCHG EAX,EDX ; Stack size is bigger than 4G. StoH EDI,Size=8 XCHG EDX,EAX .46: StoH EDI,Size=8 SUB EDI,ECX BufferStore [%MsgBuffer],ECX,EDI BufferStoreByte [%MsgBuffer],'h' .49: BufferStoreWord [%MsgBuffer],0x0A0D ; Terminate ListMap header line. BufferRetrieve [EBX+PGM.SegOrdBuffer] ; List groups and segments, one per line. SHR ECX,2 JZ .90: .52: LODSD ; The loop .52: .. .85: displays all ECX groups and their segments. .55: PUSH ECX,ESI MOV ESI,EAX ; ^SSS. JNSt [ESI+SSS.Status],sssGroup|sssSegment,.85: ; Omit extern pseudosegments. BufferStore$ [%MsgBuffer],=B'| ' JNSt [ESI+SSS.Status],sssSegment,.58: BufferStoreWord [%MsgBuffer],' ' ; Indent segment by two spaces against its group. .58: BufferStoreByte [%MsgBuffer],'[' BufferStore [%MsgBuffer],[ESI+SSS.NamePtr],[ESI+SSS.NameSize] BufferStore$ [%MsgBuffer],=B'],FA=' ; Display File Address. MOV EAX,[ESI+SSS.BottomFA] LEA EDI,[%Number] MOV EDX,EDI StoH EDI,Size=8 SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI MOV EBX,[%Pgm] MOV EAX,[EBX+PGM.Pgmopt.Status] AND AL,pgmoptFormatMask .VA?RVA:PROC ; Create namespace for dispatching of local .pgmopt* labels. Dispatch AL,pgmoptBIN,pgmoptBOOT,pgmoptPE,pgmoptDLL,pgmoptELFX ; Program formats which link at absolute VA rather than RVA. BufferStore$ [%MsgBuffer],=B'h,RVA=' ; Other formats link at RVA relative to ImageBase. JMPS PgmListMap.63: .pgmoptBIN: .pgmoptBOOT: .pgmoptPE: .pgmoptDLL: .pgmoptELFX: ENDP .VA?RVA: BufferStore$ [%MsgBuffer],=B'h,VA=' .63: LEA EDI,[%Number] MOV EDX,[ESI+SSS.BottomHigh] MOV EAX,[ESI+SSS.BottomLow] TEST EDX MOV EDX,EDI JZ .65: ; VA is displayed as 32bit or 64bit. XCHG EAX,ECX ; Temporary save EAX to ECX. MOV EAX,[ESI+SSS.BottomHigh] StoH EDI,Size=8 XCHG ECX,EAX .65: StoH EDI,Size=8 SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore$ [%MsgBuffer],=B'h,size=' LEA EDI,[%Number] MOV EAX,[ESI+SSS.TopLow] MOV EDX,[ESI+SSS.TopHigh] SUB EAX,[ESI+SSS.BottomLow] SBB EDX,[ESI+SSS.BottomHigh] LEA EDI,[%Number] MOV EDX,EDI StoH EDI,Size=8 SUB EDI,EDX PUSH EAX BufferStore [%MsgBuffer],EDX,EDI BufferStoreWord [%MsgBuffer],'h=' POP EAX LEA EDI,[%Number] MOV EDX,EDI StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI .67: JNSt [ESI+SSS.Status],sssGroup,.79: BufferStore$ [%MsgBuffer],=B',group' ; List the names of all group members. PUSH ECX,ESI MOV EDI,ESI ; Enumerate names of all segments which belong to the group EDI. MOV EBX,[%Pgm] BufferRetrieve [EBX+PGM.SegOrdBuffer] SHR ECX,2 .70: LODSD MOV EBX,EAX JNSt [EBX+SSS.Status],sssSegment,.76: CMP EDI,[EBX+SSS.GroupPtr] JNE .76: .73: BufferStoreWord [%MsgBuffer],' [' BufferStore [%MsgBuffer],[EBX+SSS.NamePtr],[EBX+SSS.NameSize] BufferStoreByte [%MsgBuffer],']' .76: LOOP .70: POP ESI,ECX JMP .82: .79: BufferStore$ [%MsgBuffer],=B',width=' MOV EBX,[ESI+SSS.Status] MOV EAX,sssWidthMask AND EAX,EBX LEA EDI,[%Number] MOV EDX,EDI SHR EAX,20 StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore$ [%MsgBuffer],=B',align=' MOV EAX,[ESI+SSS.Alignment] MOV EDI,EDX CMP EAX,8 JNA .80: ; Stronger alignment than 8 display as hexa. StoH EDI,Size=4 MOV AL,'h' STOSB JMPS .81: .80: StoD EDI .81: SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore$ [%MsgBuffer],=B',purpose=' Invoke EaBufferReserve::,PgmListMap Invoke SssPurposeToText::,[ESI+SSS.Purpose],EAX BufferRetrieve EAX DEC ECX ; Omit zero terminator. BufferStore [%MsgBuffer],ESI,ECX Invoke EaBufferRelease::,EAX .82: BufferStoreWord [%MsgBuffer],0x0A0D .85: POP ESI,ECX .88: LOOP .52: ; The next group/segment. .90: EndProcedure PgmListMap
ENDPROGRAM
statement and below ListMap.
PROGRAM LISTGLOBALS=ON
.PgmListGlobals Procedure Pgm, StmPtr MsgBuffer LocalVar ; Local copy of statement's MsgBuffer borrowed to display the list of symbols. InpBuffer LocalVar ; Temporary buffer with pointers to global symbols. OutBuffer LocalVar ; Temporary buffer with pointers to global symbols. CountGlb LocalVar ; Counter of global symbols. CountPub LocalVar ; Counter of public symbols. CountExt LocalVar ; Counter of extern symbols. CountExp LocalVar ; Counter of export symbols. CountImp LocalVar ; Counter of import symbols. Number LocalVar Size=12 ; Room for decadic format of counters. ClearLocalVar MOV ESI,[%StmPtr] MOV EBX,[%Pgm] MOV EAX,[ESI+STM.MsgBuffer] MOV [%MsgBuffer],EAX JNSt [EBX+PGM.Status],pgmEnvelope,.10: JNSt [EBX+PGM.Status],pgmEnvelDirty,.90: ; Do not display header when source envelope was not used. .10: BufferStore$ [%MsgBuffer],=B'| **** ListGlobals "' ; Display the header. BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt.OutFilePtr],[EBX+PGM.Pgmopt.OutFileSize] BufferStore$ [%MsgBuffer],=B'",Global=' Invoke EaBufferReserve::,PgmListGlobals MOV [%InpBuffer],EAX Invoke EaBufferReserve::,PgmListGlobals MOV [%OutBuffer],EAX ; Step 1: store pointers to all unresolved symbols with nonstandard (global) scope to input buffer in the loop .13: .. .16: ListGetFirst [EBX+PGM.SymList] JZ .40: .13: JSt [EAX+SYM.Status],symResolved,.16: JNSt [EAX+SYM.Status],symScopeMask,.16: ; Skip standard private symbols. BufferStoreDword [%InpBuffer],EAX .16: ListGetNext EAX JNZ .13: ; Step 2: Sort global symbols alphabeticaly by name. Invoke EaBufferSort::,[%InpBuffer] ; Step 3: merge public and extern/import symbols with the same name (they may have been left unresolved), ; count and copy them to the output buffer in the loop .20: .. .43:. BufferRetrieve [%InpBuffer] LEA EDX,[ESI+ECX] ; End of pointer array. .20: CMP ESI,EDX JNB .40: LODSD CMP ESI,EDX JNB .26: MOV EBX,[ESI] ; EAX and EBX are now pointers to neighbour symbols, perhaps with the same name. Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],[EBX+SYM.NamePtr],[EBX+SYM.NameSize] JNE .26: ; Both symbols EAX and EBX have the same name. Let's find which is the public|export one. ADD ESI,4 ; Skip the 2nd pointer because they will be merged. JSt [EAX+SYM.Status],symPublic|symExport,.23: XCHG EAX,EBX .23: MOV EBX,[EBX+SYM.Status] ; EAX is public|export, EBX is extern|import. AND EBX,symImport+symExtern SetSt EBX,symGlobal SetSt [EAX+SYM.Status],EBX ; Symbol EAX will be listed as global, too. .26: BufferStoreDword [%OutBuffer],EAX .30: ; Update global/public/extern/export/import counter. MOV EBX,[EAX+SYM.Status] LEA ECX,[%CountExp] JSt EBX,symExport,.36: ; Count as 'X'. LEA ECX,[%CountImp] JSt EBX,symImport,.36: ; Count as 'I'. LEA ECX,[%CountPub] JNSt EBX,symPublic,.32: JNSt EBX,symExtern,.36: ; Count as 'P' if not external, too. JMP .34: ; Count as 'G', i.e. public and external. .32: LEA ECX,[%CountExt] JSt EBX,symExtern,.36: ; Count as 'E'. .34: LEA ECX,[%CountGlb] .36: INCD [ECX] ; Increment one of five counters. JMP .20: ; The next symbol. .40: MOV EAX,[%CountGlb] LEA EDI,[%Number] MOV EDX,EDI StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore$ [%MsgBuffer],=B',Public=' MOV EAX,[%CountPub] LEA EDI,[%Number] StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore$ [%MsgBuffer],=B',Extern=' MOV EAX,[%CountExt] LEA EDI,[%Number] StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore$ [%MsgBuffer],=B',eXport=' MOV EAX,[%CountExp] LEA EDI,[%Number] StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore$ [%MsgBuffer],=B',Import=' MOV EAX,[%CountImp] LEA EDI,[%Number] StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStoreWord [%MsgBuffer],0x0A0D ; List symbols, sorted by name. BufferRetrieve [%OutBuffer] SAR ECX,2 JZ .80: .43: LODSD ; List properties of global symbol EAX. PUSH ECX,ESI MOV EBX,EAX BufferStore$ [%MsgBuffer],=B'| ' BufferStore [%MsgBuffer],[EBX+SYM.NamePtr],[EBX+SYM.NameSize] JSt [EBX+SYM.Status],symForwarded,.68: JNSt [EBX+SYM.Status],symImport,.53: JNSt [EBX+SYM.Status],symImportedByOrd,.46: ; Symbol imported by ordinal number will be listed as Name(OrdinalNr). BufferStoreByte [%MsgBuffer],'(' ; List ordinal number in parenthesis. MOV EAX,[EBX+SYM.OrdinalNr] LEA EDI,[%Number] MOV EDX,EDI StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI JMP .50: .46: MOV ECX,[EBX+SYM.InterNameSize] JECXZ .53: ; If internal name is empty, do not display it. ; Symbol imported by name will be listed as Name{InterName} if Name<>InterName. Compare [EBX+SYM.NamePtr],[EBX+SYM.NameSize],[EBX+SYM.InterNamePtr],ECX JE .53: ; Do not display internal name when it is empty or the same. BufferStoreByte [%MsgBuffer],'{' ; List internal name in curly brackets. BufferStore [%MsgBuffer],[EBX+SYM.InterNamePtr],[EBX+SYM.InterNameSize] .50: BufferStoreByte [%MsgBuffer],'}' .53: BufferStoreWord [%MsgBuffer],',[' ; List group name in brackets. Invoke SymFrameAddress::,EBX,[%Pgm] ; Returns EDX:EAX=offset, ECX=^group. JECXZ .56: ; Scalar symbol has empty segment name. BufferStore [%MsgBuffer],[ECX+SSS.NamePtr],[ECX+SSS.NameSize] .56: BufferStoreWord [%MsgBuffer],']:' LEA EDI,[%Number] MOV EDX,EDI StoH EDI,Size=8 SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI ; List symbol offset. BufferStoreWord [%MsgBuffer],'h,' MOV EDX,[%Pgm] MOV EAX,[EDX+PGM.Pgmopt.Status] JNSt EAX,pgmoptExecutable|pgmoptImage,.70: AND AL,pgmoptFormatMask CMP AL,pgmoptBIN JE .60: CMP AL,pgmoptPE JE .60: CMP AL,pgmoptDLL JE .60: CMP AL,pgmoptELFX JE .60: BufferStoreByte [%MsgBuffer],'R' ; Display VA= in images, RVA= in linkables. .60: BufferStore$ [%MsgBuffer],=B'VA=' .63: SUB EAX,EAX MOV ECX,[EBX+SYM.Section] JECXZ .66: MOV ECX,[ECX+SSS.SegmPtr] JECXZ .66: MOV EAX,[ECX+SSS.BottomLow] .66: ADD EAX,[EBX+SYM.OffsetLow] LEA EDI,[%Number] MOV EDX,EDI StoH EDI,Size=8 SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI ; List symbol VA. BufferStoreByte [%MsgBuffer],'h' .68: BufferStoreByte [%MsgBuffer],',' ; Display scope=. .70: BufferStore$ [%MsgBuffer],=B"scope='" MOV ECX,[EBX+SYM.Status] MOV AL,'X' JSt ECX,symExport,.76: MOV AL,'I' JSt ECX,symImport,.76: MOV AL,'P' JNSt ECX,symPublic,.72: JNSt ECX,symExtern,.76: JMP .74: .72: MOV AL,'E' JSt ECX,symExtern,.76: .74: MOV AL,'G' .76: MOV AH,"'" BufferStoreWord [%MsgBuffer],EAX ; Display lib= of imported and forwarded symbols. JNSt [EBX+SYM.Status],symImport|symForwarded,.78: BufferStore$ [%MsgBuffer],=B',lib="' MOV ECX,[EBX+SYM.DllNameSize] MOV ESI,[EBX+SYM.DllNamePtr] TEST ECX JNZ .77: MOV ESI,=B"%EuroasmDefaultDllName" MOV ECX,%EuroasmDefaultDllNameSize .77: BufferStore [%MsgBuffer],ESI,ECX BufferStoreByte [%MsgBuffer],'"' .78: ; Display fwd= of forwarded symbols. JNSt [EBX+SYM.Status],symForwarded,.79: BufferStore$ [%MsgBuffer],=B',fwd=' BufferStore [%MsgBuffer],[EBX+SYM.InterNamePtr],[EBX+SYM.InterNameSize] .79: BufferStoreWord [%MsgBuffer],0x0A0D POP ESI,ECX LOOP .43: ; The next global symbol. .80: Invoke EaBufferRelease::,[%OutBuffer] Invoke EaBufferRelease::,[%InpBuffer] .90:EndProcedure PgmListGlobals
Program.EntryExp
is evaluated.PgmEvalEntry Procedure PgmPtr MOV EBX,[%PgmPtr] LEA EDI,[EBX+PGM.EntryExp] MOV ECX,[EBX+PGM.Pgmopt.EntrySize] MOV ESI,[EBX+PGM.Pgmopt.EntryPtr] StripSpaces ESI,ECX JECXZ .90: ; If the program optionENTRY=
is empty, leavePGM.EntryExp
empty as well. Invoke ExpEval::,EDI,ESI,ECX,[EBX+PGM.CurrentStm] Invoke ExpReportError::,EDI JC .90: MOV ECX,[EDI+EXP.Sym] JECXZ .90: SetSt [ECX+SYM.Status],symEntry+symGlobal+symUsed+symProc ; If theENTRY=
referred some symbol, set it a global scope. MOV EAX,[ECX+SYM.SymbPtr] TEST EAX JZ .30: MOV ECX,EAX .30: MOV EAX,[ECX+SYM.Section] JNSt [EAX+SSS.Status],sssLinked,.50: MOV EDX,[EAX+SSS.BottomHigh] MOV EAX,[EAX+SSS.BottomLow] ADD EAX,[ECX+SYM.OffsetLow] ADC EDX,[ECX+SYM.OffsetHigh] MOV [EDI+EXP.Low],EAX MOV [EDI+EXP.High],EDX MOVD [EDI+EXP.Seg],0 MOVB [EDI+EXP.Status],'N' ; When the entry symbol is in linked segment, change address symbol to a numeric one. .50: JNSt [ECX+SYM.Status],symExtern,.90: MOV EDX,[ECX+SYM.Section] ; Symbol ECX is extern, try to resolve it. MOV ECX,[EDX+SSS.SymPtr] JECXZ .90: MOV [EDI+EXP.Sym],ECX ; Replace the extern symbol with its homonymous public symbol. SetSt [ECX+SYM.Status],symEntry+symGlobal+symUsed+symProc ; If theENTRY=
referred some symbol, set it a global scope. .90:EndProcedure PgmEvalEntry
PROGRAM ENTRY=
was empty or if it evaluated with error.PGM.EntryExp
contains PROGRAM ENTRY=
parameter evaluated by
PgmEvalEntry.PgmGetEntryAddress Procedure PgmPtr MOV EBX,[%PgmPtr] XOR EAX,EAX XOR EDX,EDX CMP [EBX+PGM.Pgmopt.EntrySize],EAX JNZ .Ent: JNSt [EBX+PGM.Pgmopt.Status],pgmoptExecutable,.End: JSt [EBX+PGM.Status],pgmEntryReported,.Err: Msg '7710' ; "PROGRAM ENTRY=" is mandatory in this program format. SetSt [EBX+PGM.Status],pgmEntryReported JMP .Err: ; PROGRAM ENTRY="!1S" symbol was not found. .Ent:Invoke PgmEvalEntry,EBX LEA ESI,[EBX+PGM.EntryExp] MOV ECX,[ESI+EXP.Status] Dispatch CL,'A','N' JSt [EBX+PGM.Status],pgmEntryReported,.Err: LEA ECX,[EBX+PGM.Pgmopt.EntryPtr] Msg '7711',ECX ; PROGRAM ENTRY="!1S" symbol was not found. SetSt [EBX+PGM.Status],pgmEntryReported .Err:STC ; Other ENTRY evaluated types than number or symbol are wrong. JMP .End: ; Entry was evaluated to error status '#' (already reported) or other nonsense. .A: MOV ECX,[ESI+EXP.Seg] JECXZ .End: ADD EAX,[ECX+SSS.BottomLow] ADC EDX,[ECX+SSS.BottomHigh] .N: ADD EAX,[ESI+EXP.Low] ADC EDX,[ESI+EXP.High] .End:MOV [%ReturnEAX],EAX MOV [%ReturnEDX],EDX EndProcedure PgmGetEntryAddress
PgmCombine will merge linkable programs (modules) which were loaded from disk file(s) by
PfLoad as a result of pseudoinstruction LINK
. Pointers to those modules has been already stored on Pgm.ModulePgmList
.
This procedure combines only modules marked as
pgmSelected
in their PGM.Status by PgmSelectModules (smart linking).
Pointers to combined modules are left on Pgm.ModulePgmList
but the emitted contents
of homonymous public segments is merged to BasePgm.
BasePgm.ModulePgmList
are combined with the BasePgm.PgmCombine Procedure BasePgm MOV EBX,[%BasePgm] ; Step 1: Merge SSS externs, segments and groups from loaded modules to BasePgm, including their emited contents and relocations. ; Bottom and Top of merged public segment is elevated, but symbols and relocations are not copied yet to the BasePgm. ListGetFirst [EBX+PGM.ModulePgmList] JZ .90: ; Do nothing when no module is linked to the BasePgm. .10:MOV EDI,EAX ; EDI is now one of the modules which were loaded by LINK statement. JNSt [EDI+PGM.Status],pgmSelected,.18: ; Skip the unused library module (smart linking). ListGetFirst [EDI+PGM.SssList] ; EAX is now one of the SSS objects of linked module. JZ .18: .14:Invoke SssCombine::,EAX,EBX ; Combine linked SSS object EAX to the main program EBX (also merge its Emit&RelocBuffer unchanged). .16:ListGetNext EAX JNZ .14: ; The next SSS object of linked module EDI. .18:ListGetNext EDI ; The next linked module. JNZ .10: Invoke SssResolve::,EBX ; Update references SSS.SegmPtr, SSS.GroupPtr, SSS.SymPtr in all SSS objects which were combined to BasePgm. ListGetFirst [EBX+PGM.ModulePgmList] .38:MOV EDI,EAX ; EDI points to the combined module. JNSt [EDI+PGM.Status],pgmSelected,.45: ; Skip the unused library module (smart linking). ListGetFirst [EDI+PGM.SymList] JZ .45: .40:Invoke SymCombine::,EAX,EBX ; Reassign symbols from linked modules to BasePgm, elevate their offsets. ListGetNext EAX ; The next symbol. JNZ .40: .45:ListGetNext EDI ; The next linked module. JNZ .38: ; Step 2: update relocations in all segments of the base program EBX. ListGetFirst [EBX+PGM.SssList] ; Resolve relocations from base program. JZ .36: .20:BufferRetrieve [EAX+SSS.RelocBuffer] ; Relocations from linked modules were copied here verbatim. JC .32: ADD ECX,ESI ; ECX is now the end of array of RELOC records. .24:CMP ESI,ECX JNB .32: JSt [ESI+RELOC.Status],relocIgnore,.28: Invoke RelocCombine::,ESI,EBX .28:ADD ESI,SIZE# RELOC JMP .24: ; The next RELOC record. .32:ListGetNext EAX ; The next segment. JNZ .20: .36: ; Update virtual addresses of all groups combined in BasePgm. ListGetFirst [EBX+PGM.SssList] JZ .60: .50:JNSt [EAX+SSS.Status],sssGroup, .55: Invoke SssResizeGroup::,EAX,EBX .55:ListGetNext EAX ; The next SSS. JNZ.50: .60:; Step 4: combine entry point from linked modules (if any) in its unevaluated source form, and copy it to the BasePgm. ListGetFirst [EBX+PGM.ModulePgmList] .65:MOV EDI,EAX ; EDI points to the linked module. JNSt [EDI+PGM.Status],pgmSelected,.75: ; Skip the unused library module (smart linking). MOV ECX,[EDI+PGM.Pgmopt.EntrySize] JECXZ .75: ; Skip when the module EDI has no entry point in source form. MOV EAX,[EBX+PGM.Pgmopt.EntrySize] MOV ESI,[EDI+PGM.Pgmopt.EntryPtr] StripColons ESI,ECX TEST EAX ; Check if ENTRY= was already specified in the base program EBX. JZ .70: ; Skip when the base pgm EBX has vacant entry. MOV EDX,[EBX+PGM.Pgmopt.EntryPtr] StripColons EDX,EAX Compare ESI,ECX,EDX,EAX JE .75: ; Do nothing when ENTRY= in module EDI is identical with ENTRY= in the base program EBX. .W3925: LEA ECX,[EDI+PGM.Pgmopt.EntryPtr] LEA EDX,[EBX+PGM.Pgmopt.EntryPtr] Msg '3925',ECX,EDI,EDX,EBX ; Entry "!1S" specified in linkable module "!2S" collides with the entry "!3S" in "!4S". Ignored. JMPS .75: .70:MOV [EBX+PGM.Pgmopt.EntryPtr],ESI ; Copy the entry point when it wasn't specified in the base program yet. MOVW [EBX+ECX+PGM.Pgmopt.EntryPtr],'::' ; Append two colons to entry name INC ECX,ECX MOV [EBX+PGM.Pgmopt.EntrySize],ECX .75:ListGetNext EDI ; The next linked module. JNZ .65: ;; ; Step 5: Resolve symbols from loaded modules. .80:MOV [EBX+PGM.ModulePgmList],0 ; Invalidate the list of combined modules. .90:Invoke PgmResizeGroups,EBX EndProcedure PgmCombine
PgmDetectImportModule tries to detect if the Pgm (just loaded module) is
a module from import library, i.e. if it contains import info only,
and it will set the flag Pgm.Status:pgmImpLibMember
in this case.
Module is pure import library member if
Import libraries might have been created by a third-party librarian, such as ALIB.exe, The only code they have is an indirect proxy jump through IAT table to dynamically loaded function, and a public symbol referring to this proxy jump. Imported module may also contain an extern symbol with decorated internal name.
Pgm.Status
.PgmDetectImportModule Procedure Pgm MOV EBX,[%Pgm] RstSt [EBX+PGM.Status],pgmImpLibMember ; First assume it is an ordinary object module. SUB ECX,ECX ; Count symbols in module EBX into counter ECX. ListGetFirst [EBX+PGM.SymList] JZ .90: ; No symbols exits, its not an import library member. .10:INC ECX ListGetNext EAX JNZ .10: CMP ECX,3 JA .90: ; Too many symbols, not an import library member. SUB ECX,ECX ; Count segments in module EBX into counter ECX. ListGetFirst [EBX+PGM.SssList] JZ .90: ; No segments exist, its not an import library member. .20:JNSt [EAX+SSS.Status],sssSegment,.30: INC ECX MOV EDX,EAX ; Remember the segment in EDX. .30:ListGetNext EAX JNZ .20: CMP ECX,1 JNE .90: ; Too many segments, its not an import library member. MOV ECX,[EDX+SSS.NameSize] ; EDX is the one and only segment. MOV ESI,[EDX+SSS.NamePtr] ; Convert the segment name ESI,ECX to uppercase. CMP ECX,5 JB .90: ; Segment name is too short, not IDATA/IMPORT. Invoke EaBufferReserve::,PgmDetectImportModule MOV EBX,EAX BufferStore EBX,ESI,ECX BufferRetrieve EBX MOV EDI,ESI .40:LODSB ; Convert the letter to upper case. CMP AL,'a' JB .50: CMP AL,'z' JA .50: SUB AL,'a'-'A' .50:STOSB LOOP .40: BufferRetrieve EBX ; ESI,ECX is now uppercased segment name. Invoke EaBufferRelease::,EBX MOV EDI,ESI ; Search the name for 'IMPORT' or 'IDATA'. MOV AL,'I' .60:REPNE SCASB JNE .90: CMP ECX,4 JB .90: CMPD [EDI],'DATA' JE .70: CMP ECX,5 JB .90: CMPB [EDI],'M' JNE .60: CMPD [EDI+1],'PORT' JNE .60: .70:BufferRetrieve [EDX+SSS.EmitBuffer] CMP ECX,7 JA .90: CMP CL,6 JB .90: MOV EBX,[%Pgm] ; Segment name contains 'IMPORT' or 'IDATA' SetSt [EBX+PGM.Status],pgmImpLibMember ; and its code is 6..7 bytes long. Module is an import library member. .90:EndProcedure PgmDetectImportModule
PgmCheckDirty Procedure Pgm MOV EBX,[%Pgm] MOV ECX,[EBX+PGM.LinkFilesNr] JECXZ .10: STC JMP .90: ; Dirty. .10:ListGetFirst [EBX+PGM.SssList] JZ .NotDirty: .20:Invoke SssCheckDirty::,EAX,EBX JC .90: ; Dirty ListGetNext EAX JNZ .20: .NotDirty: CLC .90: EndProcedure PgmCheckDirty
PgmSelectModules provides smart linking
of modules to executable file. It will inspect each module loaded on
Program.ModulePgmList
whether some of its public|export symbols
is referenced by some extern|import symbol defined in the base program
or in other non-import modules, and vice versa. Such module will be marked
pgmSelected
for later combining. Unreferenced modules from
libraries will not be combined and linked.
When the base Program output format is a library (FORMAT=LIBOMF|LIBCOF|ELFSO) instead of executable,
all its loaded modules will be marked pgmSelected
regardless if they are referenced or not (perhaps they will be
referenced later, when the base Program (library) would get linked to an executable format).
When any of the linked programs is a standalone module (FORMAT=COFF|OMF|ELF) and not a library member (pgmoptLibMember=0
),
it will be marked pgmSelected
regardless if its symbols are referenced or not
(because it was explicitly required with LINK module.obj
).
PGM.Status:pgmSelected
.PgmSelectModules Procedure Program PubBuf LocalVar ; Pointer to a buffer which contains DWORD pointers to all public|export symbols. ExtBuf LocalVar ; Pointer to a buffer which contains DWORD pointers to all extern|import symbols. Invoke EaBufferReserve::,PgmSelectModules MOV [%PubBuf],EAX MOV ECX,EAX Invoke EaBufferReserve::,PgmSelectModules MOV [%ExtBuf],EAX MOV EDX,EAX MOV EBX,[%Program] SetSt [EBX+PGM.Status],pgmSelected ; The base program is always selected. JNSt [EBX+PGM.Pgmopt.Status],pgmoptLibrary,.14: ; Skip when the base program is not a library. ListGetFirst [EBX+PGM.ModulePgmList] ; Mark all modules as pgmSelected when a library is being compiled. JZ .90: ; Nothing to do when no module was loaded. .10:SetSt [EAX+PGM.Status],pgmSelected ListGetNext EAX JNZ .10: JMP .90: .14:; Collect pointers to global symbols into ExtBuf EDX and PubBuf ECX from all non-import-only modules. Invoke PgmStoreGlobalSymbols,EBX,ECX,EDX ; Collect globals from the base program EBX. ListGetFirst [EBX+PGM.ModulePgmList] JZ .90: ; Nothing to do when no module was loaded. .18:JSt [EAX+PGM.Status],pgmImpLibMember,.22: ; Ignore pure import library members. Invoke PgmStoreGlobalSymbols,EAX,ECX,EDX ; Collect globals from the loaded module EAX. .22:ListGetNext EAX JNZ .18: .26:; Inspect each module and flag the module as pgmSelected if ; any of its public symbols is referenced by any symbol in ExtBuf, or ; if the module is not a library (it is a standalone COFF|OMF|ELF file). ListGetFirst [EBX+PGM.ModulePgmList] .30:MOV EBX,EAX ; EBX is now the inspected module. JSt [EBX+PGM.Status],pgmSelected,.54: ; Skip inspection when its already referenced. JSt [EBX+PGM.Pgmopt.Status],pgmoptLibMember,.34: SetSt [EBX+PGM.Status],pgmSelected ; Select loaded standalone COFF|OMF modules. JMP .54: .34:ListGetFirst [EBX+PGM.SymList] JZ .54: ; Skip the module EBX when it has no symbols. .38:MOV EDI,EAX ; EDI is now a symbol from module EBX. JNSt [EDI+SYM.Status],symPublic|symExport,.50: BufferRetrieve [%ExtBuf] ; EDI is public symbol in loaded module EBX. Find if it is referenced. SHR ECX,2 JZ .58: ; Skip when no external symbols exist. .42:LODSD ; EAX is now one of external/imported symbols. Compare names of symbols EAX and EDI. Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],[EDI+SYM.NamePtr],[EDI+SYM.NameSize] JNE .46: SetSt [EBX+PGM.Status],pgmSelected ; The module was referenced, it will be combined and linked. JMP .54: ; Break further inspecting of other symbols in module EBX. .46:LOOP .42: .50:ListGetNext EDI ; The next symbol. JNZ .38: .54:ListGetNext EBX JNZ .30: ; The next loaded module. .58:; Collects pointers to global symbols into ExtBuf EDX and PubBuf ECX again but this time from referenced modules only. MOV EBX,[%Program] MOV ECX,[%PubBuf] MOV EDX,[%ExtBuf] BufferClear ECX BufferClear EDX Invoke PgmStoreGlobalSymbols,EBX,ECX,EDX ; Collect globals from the base program. ListGetFirst [EBX+PGM.ModulePgmList] .62:JNSt [EAX+PGM.Status],pgmSelected,.66: ; Skip not referenced modules. Invoke PgmStoreGlobalSymbols,EAX,ECX,EDX ; Collect globals from the loaded module. .66:ListGetNext EAX JNZ .62: ; The next module. ; Inspect each module and flags it as pgmSelected if any of its public|import symbols is referenced by any symbol in ExtBuf. ListGetFirst [EBX+PGM.ModulePgmList] .70:MOV EBX,EAX ; EBX is now the inspected module. ListGetFirst [EBX+PGM.SymList] JZ .88: ; Skip the module EBX when it has no symbols. .74:MOV EDI,EAX ; EDI is now a symbol from module EBX. JNSt [EDI+SYM.Status],symPublic|symImport,.86: BufferRetrieve [%ExtBuf] ; EDI is public|import symbol in loaded module EBX. Check if it is referenced. SHR ECX,2 JZ .86: .78:LODSD ; EAX is now one of external/imported symbols. Compare names of symbols EAX and EDI. Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],[EDI+SYM.NamePtr],[EDI+SYM.NameSize] JNE .82: SetSt [EBX+PGM.Status],pgmSelected ; The module was referenced, it will be combined and linked. JMP .88: ; Break further inspecting of other symbols in module EBX. .82:LOOP .78: .86:ListGetNext EDI ; The next symbol. JNZ .74: .88: ListGetNext EBX ; The next loaded module. JNZ .70: .90: Invoke EaBufferRelease::,[%ExtBuf] Invoke EaBufferRelease::,[%PubBuf] EndProcedure PgmSelectModules
PgmStoreGlobalSymbols Procedure PgmPtr,PubBuffer,ExtBuffer MOV EDI,[%PgmPtr] MOV ECX,[%PubBuffer] MOV EDX,[%ExtBuffer] ListGetFirst [EDI+PGM.SymList] JZ .90: .30: JNSt [EAX+SYM.Status],symExtern|symImport,.50: BufferStoreDword EDX,EAX .50: JNSt [EAX+SYM.Status],symPublic|symExport,.80: BufferStoreDword ECX,EAX .80: ListGetNext EAX ; The next symbol. JNZ .30: .90: EndProcedure PgmStoreGlobalSymbols
BasePgm.ModulePgmList
. The module is marked pgmImpModule
and it contains only the imported symbol.
symImport+'A'
.
PgmCreateImportModule Procedure BasePgmPtr, DllNamePtr, DllNameSize, OrdinalNr, \ SymStatus, SymNamePtr, SymNameSize, \ SymInterNamePtr, SymInterNameSize MOV EBX,[%BasePgmPtr] MOV EDX,[EBX+PGM.Pool] ListNew [EBX+PGM.ModulePgmList],Zeroed=yes MOV EDI,EAX MOV [EDI+PGM.Pool],EDX ; Let module's pool parasite on BasePgm.Pool. SetSt [EDI+PGM.Status],pgmImpLibMember MOV EAX,pgmoptWidthMask AND EAX,[EBX+PGM.Pgmopt.Status] OR EAX,pgmoptLibMember SetSt [EDI+PGM.Pgmopt.Status],EAX ListCreate EDX,SIZE#SSS MOV [EDI+PGM.SssList],EAX ListCreate EDX,SIZE#SYM MOV [EDI+PGM.SymList],EAX ListNew EAX,Zeroed=yes MOV EBX,EAX ; EBX is now pointer to the imported symbol. MOV EAX,[%SymStatus] SetSt [EBX+SYM.Status],EAX MOV ESI,[%DllNamePtr] MOV ECX,[%DllNameSize] PoolStore EDX,ESI,ECX MOV [EBX+SYM.DllNamePtr],EAX MOV [EBX+SYM.DllNameSize],ECX MOV ESI,[%SymNamePtr] MOV ECX,[%SymNameSize] PoolStore EDX,ESI,ECX MOV [EDI+PGM.NamePtr],EAX MOV [EDI+PGM.NameSize],ECX MOV [EBX+SYM.NamePtr],EAX MOV [EBX+SYM.NameSize],ECX MOV [EBX+SYM.InterNamePtr],EAX MOV [EBX+SYM.InterNameSize],ECX MOV ESI,[%SymInterNamePtr] MOV ECX,[%SymInterNameSize] JECXZ .80: PoolStore EDX,ESI,ECX MOV [EBX+SYM.InterNamePtr],EAX MOV [EBX+SYM.InterNameSize],ECX .80:MOV EAX,[%OrdinalNr] MOV [EBX+SYM.OrdinalNr],EAX Invoke SssCreateExtern::,EBX,EDI EndProcedure PgmCreateImportModule
PgmStreamImage stores the emitted contents of all initialized segments to the %Stream in the order specified on
Pgm.SegOrdBuffer
by PgmOrderSegments.
All segments should be linked by PgmLink and their SSS.BottomFA
set.
Segment FileAddress will be used to stuff the stream by 0 bytes (or with NOP if both segments have
PURPOSE=CODE
).
PgmStreamImage Procedure Pgm, Stream Purpose LocalVar ; Purpose of the current segment. SssPtr LocalVar ; Pointer to the array of pointers to SSS. SssPtrTop LocalVar ; Pointer behind the array of pointers to SSS. XOR EDX,EDX MOV EBX,[%Pgm] MOV [%Purpose],EDX BufferRetrieve [EBX+PGM.SegOrdBuffer] ADD ECX,ESI MOV [%SssPtr],ESI MOV [%SssPtrTop],ECX MOV EBX,[%Stream] .20:MOV ESI,[%SssPtr] CMP ESI,[%SssPtrTop] ; ESI points to an array of pointers. JNB .90: LODSD MOV [%SssPtr],ESI JNSt [EAX+SSS.Status],sssSegment,.20: JNSt [EAX+SSS.Status],sssNotBSS,.20: .28: ; EAX is an emitting segment (CODE,RODATA,DATA) or special segment (SYMBOLS, STRINGS etc). MOV EDI,EAX ; EDI is the segment with initialized contents. MOV EAX,[EDI+SSS.Purpose] XOR EDX,EDX ; DL is alignment stuff 0x00 or 0x90. XCHG EAX,[%Purpose] JNSt EAX,sssPurposeCODE,.30: JNSt [%Purpose],sssPurposeCODE,.30: MOV DL,0x90 ; Stuff with NOP when both EDI and the previous segment has purpose CODE. .30: StreamGetSize EBX ; Load EAX with the number of already streamed bytes. MOV ECX,[EDI+SSS.BottomFA] SUB ECX,EAX JZ .50: JB .F9341: ; Incorrectly linked program, abort. .40: StreamStoreByte EBX,DL ; Intersection alignment stuff. LOOP .40: .50: BufferRetrieve [EDI+SSS.EmitBuffer] StreamStore [%Stream],ESI,ECX .80: JMP .20: ; The next segment. .F9341:Msg '9341',EAX ; Internal error: invalid linking at file offset !1Hh. .90:EndProcedure PgmStreamImage
Group.Bottom
will be the lowest bottom and Group.Top
the highest top of all its segments.
Group.Bottom
will be 0 in program format COM, as the addressing frame respects PSP (256 bytes). Similarily in format BOOT.
Group.BottomFA
will be the lowest file bottom and Group.TopFA
the highest file top of all its segments (this is used in ELFX format).
SSS.Status:sssNotBSS
flag.
It is set when at least one its segment has initialized contents.
PgmResizeGroups Procedure Prog MOV EBX,[%Prog] ListGetFirst [EBX+PGM.SssList] ; Resize groups in the base program. JZ .30: XOR ECX,ECX .10:JNSt [EAX+SSS.Status],sssGroup, .20: Invoke SssResizeGroup::,EAX,EBX .20:ListGetNext EAX JNZ .10: .30:ListGetFirst [EBX+PGM.ModulePgmList] ; Resize groups in the loaded modules. JZ .90: .40:MOV EDI,EAX ListGetFirst [EDI+PGM.SssList] JZ .70: .50:JNSt [EAX+SSS.Status],sssGroup, .60: Invoke SssResizeGroup::,EAX,EBX .60:ListGetNext EAX JNZ .50: .70:ListGetNext EDI JNZ .40: .90:EndProcedure PgmResizeGroups
PgmGroupByModel will assign orphaned segment to groups in NEAR memory models.
PgmGroupByModel is invoked when linkable modules have been already combined into this Program.
PgmGroupByModel Procedure Program MOV EBX,[%Program] MOV EAX,[EBX+PGM.Pgmopt.Status] AND EAX,pgmoptModelMask Dispatch EAX,pgmoptFLAT,pgmoptSMALL,pgmoptMEDIUM,pgmoptCOMPACT,pgmoptLARGE,pgmoptHUGE .pgmopTINY: ; Regroup all orphaned segments to one common group. MOV ECX,sssPurposeRegular CALL .Regroup: JMP .pgmoptFLAT: .Regroup:PROC1 ; Find or create a group with purpose ECX and assign nongrouped segments with purpose ECX to the group. ; Input: EBX=^PGM, ECX=purpose ListGetFirst [EBX+PGM.SssList] JZ .R9: MOV EDX,6 ; Size of the new group name[?GROUP]
. .R1:JNSt [EAX+SSS.Status],sssGroup,.R2: JSt [EAX+SSS.Purpose],ECX,.R5: .R2:ListGetNext EAX JNZ .R1: CMP ECX,sssPurposeRegular JNE .R3: MOV ESI,=B"COMGROUP" INC EDX,EDX ; Size of the new group name[COMGROUP]
. JMP .R4: .R3:Dispatch ECX,sssPurposeCODE,sssPurposeSTACK MOV ESI,=B"DGROUP" ; sssPurposeDATA|RODATA|BSS. JMPS .R4: .sssPurposeCODE: MOV ESI,=B"CGROUP" JMPS .R4: .sssPurposeSTACK: MOV ESI,=B"SGROUP" .R4:Invoke SssCreateGroup::,[EBX+PGM.CurrentStm],ESI,EDX,sssGroup+sssImplicit,ECX Invoke SymCreateSe::,EAX,EBX .R5:MOV EDI,EAX ListGetFirst [EBX+PGM.SssList] ; EDI is the group with the purpose ECX. .R6:JNSt [EAX+SSS.Status],sssSegment,.R8: ; Assign all ungrouped segments with the purpose ECX to the group EDI. JNSt [EAX+SSS.Purpose],ECX,.R8: MOV EDX,[EAX+SSS.GroupPtr] TEST EDX JNZ .R8: MOV [EAX+SSS.GroupPtr],EDI ; Assign ungrouped segment EAX to the group EDI. .R8:ListGetNext EAX JNZ .R6: .R9:RET ENDP1 .Regroup: .pgmoptSMALL: ; All segments are NEAR. .pgmoptCOMPACT: ; Code segments are NEAR, data segments are FAR. MOV ECX,sssPurposeCODE CALL .Regroup: JSt [EBX+PGM.Pgmopt.Status],pgmoptCOMPACT,.pgmoptLARGE: .pgmoptMEDIUM: ; Data segments are NEAR, code segments are FAR. MOV ECX,sssPurposeDATA+sssPurposeRODATA+sssPurposeBSS CALL .Regroup: .pgmoptLARGE: ; Code and data segments are FAR. .pgmoptHUGE: MOV ECX,sssPurposeSTACK ; Stack segments are NEAR in all models. CALL .Regroup ; Find or create [SGROUP] and assign ungrouped stack segments to it. .pgmoptFLAT: ; Do not group any segments in FLAT model. ListGetFirst [EBX+PGM.SssList] ; Finally, merge VA, FA, purpose and sssNotBSS status from all group's segments. JZ .90: .50:JNSt [EAX+SSS.Status],sssGroup,.70: Invoke SssResizeGroup::,EAX,EBX .70:ListGetNext EAX JNZ .50: .90:EndProcedure PgmGroupByModel
PgmLinkSections Procedure Pgm MOV EBX,[%Pgm] ListGetFirst [EBX+PGM.SssList] JZ .60: .10:JNSt [EAX+SSS.Status],sssSegment,.15: ; Pass 1: link VA of sections, concatenate contents. Invoke SssOrderSections::,EAX,EBX Invoke SssLinkSections::, EAX .15:ListGetNext EAX JNZ .10: ListGetFirst [EBX+PGM.SssList] ; Pass 2: update relocations in segments. .20:JNSt [EAX+SSS.Status],sssSegment,.50: BufferRetrieve [EAX+SSS.RelocBuffer] ADD ECX,ESI .30:CMP ESI,ECX JNB .50: ; When there are no more relocations in segment EAX. PUSH ECX MOV ECX,[ESI+RELOC.Section] MOV EDI,[ECX+SSS.BottomLow] MOV EDX,[ECX+SSS.BottomHigh] MOV ECX,[ECX+SSS.SegmPtr] JECXZ .35: SUB EDI,[ECX+SSS.BottomLow] SBB EDX,[ECX+SSS.BottomHigh] ADD [ESI+RELOC.OrgLow],EDI ADC [ESI+RELOC.OrgHigh],EDX MOV [ESI+RELOC.Section],ECX .35: MOV ECX,[ESI+RELOC.Status] JSt ECX,relocIgnore,.40: AND ECX,relocTypeMask MOV ECX,[ESI+RELOC.Symbol] JECXZ .40: MOV ECX,[ECX+SYM.Section] JECXZ .40: MOV EDI,[ECX+SSS.BottomLow] MOV EDX,[ECX+SSS.BottomHigh] ADD [ESI+RELOC.AddendLow],EDI ADC [ESI+RELOC.AddendHigh],EDX MOV ECX,[ECX+SSS.SegmPtr] JECXZ .40: MOV ECX,[ECX+SSS.SymPtr] MOV [ESI+RELOC.Symbol],ECX .40:POP ECX ADD ESI,SIZE# RELOC JMP .30: .50:ListGetNext EAX JNZ .20: .60:ListGetFirst [EBX+PGM.SymList] ; Update symbols. JZ .90: .70:MOV ECX,[EAX+SYM.Section] JECXZ .80: JNSt [ECX+SSS.Status],sssSection,.80: MOV EBX,[ECX+SSS.SegmPtr] MOV EDI,[ECX+SSS.BottomLow] MOV EDX,[ECX+SSS.BottomHigh] ADD [EAX+SYM.OffsetLow],EDI ADC [EAX+SYM.OffsetHigh],EDX MOV [EAX+SYM.Section],EBX JNSt [EAX+SYM.Status],symSe,.80: JSt [ECX+SSS.Status],sssSegment|sssGroup,.80: RstSt [EAX+SYM.Status],symSe ; Make symSe of sssSection an ordinary symPrivate. .80:ListGetNext EAX JNZ .70: .90: EndProcedure PgmLinkSections
PgmSymResolved
and its
SYM.SymbPtr
points to the matching public (or weak) symbol in BasePgm.
BasePgm.SymList
and symbols refered in relocations RELOC.Symbol
are resolved.PgmSymResolve Procedure BasePgm RelocEnd LocalVar ; End of array of RELOC records. MOV EBX,[%BasePgm] ; Resolve symbols combined in BasePgm. ListGetFirst [EBX+PGM.SymList] JZ .90: .10:MOV EDI,EAX mov ecx,[edi+SYM.NamePtr] .15:Invoke SymResolve::,EDI,EBX ; tady to mrzne, protoze LIST of SYMs je serazeny dle abecedy a cykli kolem dokola .20:ListGetNext EDI JNZ .10: ; The next symbol in BasePgm. ; Resolve target symbols of relocations combined in BasePgm. ListGetFirst [EBX+PGM.SssList] JZ .90: .30:MOV EDX,EAX ; EDX is a segment of BasePgm. BufferRetrieve [EDX+SSS.RelocBuffer] ADD ECX,ESI MOV [%RelocEnd],ECX .40:CMP ESI,[%RelocEnd] JNB .60: ; Skip when there are no (more) relocations in segment EDX. JSt [ESI+RELOC.Status],relocResolved,.50: MOV EAX,[ESI+RELOC.Symbol] ; Resolve relocation target. TEST EAX JZ .50: MOV ECX,[EAX+SYM.SymbPtr] ; Is the symbol EAX is redirected to a public ECX? JECXZ .50: MOV [ESI+RELOC.Symbol],ECX .50:ADD ESI,SIZE# RELOC ; The next RELOC. JMP .40: .60:ListGetNext EDX JNZ .30: ; The next segment. .90:EndProcedure PgmSymResolve
RELOC.Org
is fixed up according to relocation type.
SSS.RelocBuffer
are then marked as relocResolved.PgmRelocResolve Procedure Program MOV EBX,[%Program] ListGetFirst [EBX+PGM.SssList] JZ .90: .10: JNSt [EAX+SSS.Status],sssSegment,.80: Invoke SssRelocResolve::,EAX,EBX ; Resolve all relocation in segment EAX. .80: ListGetNext EAX JNZ .10: ; The next segment. .90:EndProcedure PgmRelocResolve
Segments and groups specified in PGM.OrdBuffer
will be virtually linked to the final image of executable or linkable file.
PgmLink is invoked when all linked modules have been loaded and their pointers stored on
PGM.ModulePgmList
, all modules have been combined by PgmCombine into the main PGM
and when segment order was already established by
PgmOrderSegments.
Segment virtual size is calculated as SSS.Top - SSS.Bottom
for both initialized and uninitialized segments.
PgmLink manipulates with three members of SSS objects (groups and segments):
SSS.BottomFA
SSS.SVA
SSS.Bottom
FA alias SSS.BottomFA
of 1st segment starts at FAbottom
aligned by the program option %^FileAlign
.
FA of ELF special sections is aligned by the section's own alignment. FA of regular and PE-special sections is aligned by the greater of section's own alignment and program option FileAlign=.
SVA alias SSS.SVA
is virtual address related to the addressing frame, i.e. to the start of segment's group (or to the start of ungrouped segment),
assuming that the segment register (CS, DS, SS) was loaded with paragraph address of the adressing frame.
SVA takes memory model into account.
SVA=0 in FAR models. It is 0 in monosegment NEAR models, too, but SVA>0 when
there are more segments with the similar purpose, for instance when MODEL=SMALL and several code or data segments with different names
have been linked from other modules to the main MZ executable, or when DS=PARA# [DGROUP]
is used as a data addressing frame
and the datagroup encompasses three segments: [DGROUP] GROUP [RODATA],[DATA],[BSS]
.
Contents of all those segments is accessible without updating DS, which needs to be initialized only once
in the beginning of the program (loading DS with PARA# [DGROUP]
).
In this case SVA of segment [DATA] is increased by the aligned size of [RODATA], and SVA of [BSS] is increased
by the aligned sizes of [RODATA] plus [DATA].
SVA is used to resolve relocations in realmode programs. SVA and RVA are aligned by the greater of %^SectionAlign
and segment's own alignment.
VA alias SSS.BottomLow
in executable images is absolute segment address related to the %^ImageBase
. It is used to resolve relocations in FLAT memory models.
VA is calculated as a sum of program option %^ImageBase
plus VAbottom
, aligned by the greater of the program option
%^SectionAlign
and the segment's own alignment SSS.Alignment
. When VA is adjusted,
SSS.Status
is marked as sssLinked
, thus relocations targeting this segment are resolvable.
VA of segments in linkable modules (pgmoptLinkable
) is not modified and remains zero. Segment status sssLinked
remains reset.
PgmLink does not change the contents of SSS.EmitBuffer, SSS.RelocBuffer
,
the actual raw contents of segments is not concatenated into image here because relocations are not resolved yet.
PgmLink Procedure PgmPtr, FAbottom, VAbottom ArrayPtr LocalVar ; Pointer to the array of pointers to SSS segments. ArrayEnd LocalVar ; Behind the last pointer. Size LocalVar ; Virtual (emitted or reserved) segment size. FileAlign LocalVar ; Special ELF section Alignment or the greater of %^FileAlign and regular segment's Alignment. SectAlign LocalVar ; Greater of %^SectionAlign and segment Alignment. VALow LocalVar ; Segment virtual address when linked, VAHigh LocalVar ; including ImageBase. AssumedCS LocalVar ; SVA of code segment (relative to the assumed CS). AssumedDS LocalVar ; SVA of data segment (relative to the assumed DS). AssumedSS LocalVar ; SVA of stack segment (relative to the assumed SS). MOV EBX,[%PgmPtr] MOV ESI,[%FAbottom] ; Initialize file~ and virtual addresses. XOR EDI,EDI Invoke ExpAlign::,ESI,[EBX+PGM.Pgmopt.FileAlign],EDI ADD [%FAbottom],ECX ; Alignment stuff following the headers. MOV ESI,[%VAbottom] MOV EAX,[EBX+PGM.Pgmopt.ImageBaseLow] MOV EDX,[EBX+PGM.Pgmopt.ImageBaseHigh] ADD EAX,ESI ADC EDX,EDI Invoke ExpAlign::,EAX,[EBX+PGM.Pgmopt.SectionAlign],EDI ADD EAX,ECX ; Alignment stuff following the headers. ADC EDX,EDI MOV [%VALow],EAX MOV [%VAHigh],EDX MOV [%AssumedCS],EAX MOV [%AssumedDS],EAX MOV [%AssumedSS],EAX BufferRetrieve [EBX+PGM.SegOrdBuffer] ADD ECX,ESI MOV [%ArrayPtr],ESI MOV [%ArrayEnd],ECX .25:MOV ESI,[%ArrayPtr] ; The main loop: Link FA, SVA and VA of all segments. CMP ESI,[%ArrayEnd] JNB .80: LODSD MOV [%ArrayPtr],ESI JNSt [EAX+SSS.Status],sssSegment,.25: ; Omit groups and external pseudosegments. CMPD [EAX+SSS.NameSize],0 ; Omit the empty section []. JZ .25: MOV EDI,EAX ; Link segment EDI. JSt [EBX+PGM.Pgmopt.Status],pgmoptLinkable,.28: SetSt [EDI+SSS.Status],sssLinked .28:JSt [EDI+SSS.Purpose],sssPurposePHDR,.25: ; Do not elevate VA os [PSP]. MOV EDX,[EDI+SSS.Alignment] ; EDX is segment's own alignment. MOV ECX,[EBX+PGM.Pgmopt.SectionAlign] ; Compute effective alignment of VA.. CMP ECX,EDX JAE .30: MOV ECX,EDX .30:MOV [%SectAlign],ECX JNSt [EDI+SSS.Purpose],sssPurposeRegular|sssPurposeOptionalMask,.34: MOV ECX,[EBX+PGM.Pgmopt.FileAlign] ; Compute effective alignment of FA.. CMP ECX,EDX JAE .35: .34:MOV ECX,EDX .35:MOV [%FileAlign],ECX MOV EAX,[EDI+SSS.TopLow] ; Compute segment size in memory. MOV EDX,[EDI+SSS.TopHigh] SUB EAX,[EDI+SSS.BottomLow] SBB EDX,[EDI+SSS.BottomHigh] Msg cc=NZ,'8525',EDI ; Size of segment [!1S] exceeded 4 GB. MOV [%Size],EAX MOV ESI,EAX MOV EDX,[%FAbottom] ; Unaligned FA of the previous segment. Invoke ExpAlign::,EDX,[%FileAlign],0 ; Compute segment's file address. ADD ECX,EDX ; Alignment stuff following the headers|previous segment contents. MOV [EDI+SSS.BottomFA],ECX ; Aligned FA of this segment. JSt [EDI+SSS.Status],sssImagePrefix,.40: ; Skip when EDI is COM|BOOT pseudosegment (PSP). JNSt [EDI+SSS.Status],sssNotBSS,.40: ; Skip when ESI is BSS segment. ADD ECX,ESI ; Add %Size in the file. .40:MOV [EDI+SSS.TopFA],ECX MOV [%FAbottom],ECX ; Unaligned FA of the next segment. JSt [EBX+PGM.Pgmopt.Status],pgmoptLinkable,.43: MOV EAX,[%VALow] ; Compute segment's absolute virtual address. MOV EDX,[%VAHigh] ; EDX:EAX is unaligned VA of the previous segment. Invoke ExpAlign::,EAX,[%SectAlign],0 ; Align the virtual top of previous segment. ADD EAX,ECX ; Add returned stuff size. ADC EDX,0 JNSt [EBX+PGM.Pgmopt.Status],pgmoptELFbased,.42: MOV ECX,[EBX+PGM.Pgmopt.SectionAlign] ; Make ELF section VA and FA congruent modulo %^SectionAlign. DEC ECX AND ECX,[EDI+SSS.BottomFA] ADD EAX,ECX ADC EDX,0 .42:MOV [EDI+SSS.BottomLow],EAX MOV [EDI+SSS.BottomHigh],EDX ADD EAX,[%Size] ADC EDX,0 MOV [EDI+SSS.TopLow],EAX MOV [EDI+SSS.TopHigh],EDX MOV [%VALow],EAX MOV [%VAHigh],EDX ; Unaligned VA of the next segment. .43:MOV ECX,[EDI+SSS.GroupPtr] ; Compute segment relative offset to its frame group. MOV EAX,[EDI+SSS.BottomLow] MOV EDX,[EDI+SSS.BottomHigh] JECXZ .55: CMP EDX,[ECX+SSS.BottomHigh] JB .45: JA .50: CMP EAX,[ECX+SSS.BottomLow] JAE .50: .45:MOV [ECX+SSS.BottomLow],EAX ; Group bottom is the lowest of all its segments. MOV [ECX+SSS.BottomHigh],EDX .50:SUB EAX,[ECX+SSS.BottomLow] SBB EDX,[ECX+SSS.BottomHigh] Msg cc=NZ,'8515',EDI ; Segment [!1S] over 4 GB. MOV [EDI+SSS.SVA],EAX .55:JMP .25: .80:Invoke PgmResizeGroups,EBX ; Update VA of groups by their segments. .90:EndProcedure PgmLink
PgmOrderSegments decides about group and segment ordering in output program file.
Pointers to SSS objects will be stored to Pgm.SegOrdBuffer in the desired order.
Each SSS object is counted exactly once.
Group membership of all segments have already been established.
Ordinal number of each segment in PGM.SegOrdBuffer will be written to SSS.SegmIndex.
The order depends on SSS properties: access rights (read|write|execute), purpose, initialization status (sssNotBSS), alignment (descending).
Though many groups and segments might not exist, ordering rules are common for all program formats supported by €ASM
except for ELF-based formats, which are ordered in Pfelf*Compile.
Group and segment order will be visible in ListMap displayed in listing.
Pgm.NrOfInitSegments
.
If there are more pointers in Pgm.SegOrdBuffer
than Pgm.NrOfInitSegments
,
they all are uninitialized and their raw data may be omitted from image.PgmOrderSegments Procedure Pgm SegmIndex LocalVar ; SSS.SegmIndex (ordinal number in SHDR table). ArrayPtr LocalVar ; Pointer to the array of DWORD pointers to SSS segments. ArrayEnd LocalVar ; Behind the last pointer. Ptr1 LocalVar ; Pointer to the 1st pointer of subarray. MOV EBX,[%Pgm] MOV ECX,[EBX+PGM.SegOrdBuffer] JECXZ .10: BufferClear ECX JMPS .15: .10:BufferCreate [EBX+PGM.Pool],Size=128 ; Create PGM.SegOrdBuffer if it wasn't initialized. MOV [EBX+PGM.SegOrdBuffer],EAX .15:; Pass 1 - prepare flags. MOVD [%SegmIndex],-1 ; Segment index is 0-based. ListGetFirst [EBX+PGM.SssList] JZ .99: .20:RstSt [EAX+SSS.Status],sssOrdered+sssUsed ; Reset flag sssOrdered which guarantees that segments&groups are ordered just once .25:ListGetNext EAX ; and the groups (except for [PHDR] are not empty. JNZ .20: .30:; Pass 2 - examine used (nonempty) groups. ListGetFirst [EBX+PGM.SssList] .35:JNSt [EAX+SSS.Status],sssSegment,.40: MOV ECX,[EAX+SSS.GroupPtr] JECXZ .40: SetSt [ECX+SSS.Status],sssUsed ; Mark the group ECX of segment EAX as used. MOV EDX,[EAX+SSS.Purpose] ; Update purposes of each group. OR [ECX+SSS.Purpose],EDX ; Group ECX inherits each purpose of its segments. JNSt [EAX+SSS.Status],sssNotBSS,.40: SetSt [ECX+SSS.Status],sssNotBSS ; If segment EAX is initialized, group ECX is too. .40:ListGetNext EAX JNZ .35: .45:; Pass 3 - remove unused (empty) groups. ListGetFirst [EBX+PGM.SssList] .50:JNSt [EAX+SSS.Status],sssGroup,.55: JSt [EAX+SSS.Status],sssUsed,.55: ListRemove [EBX+PGM.SssList],EAX ; Remove unused group. JMP .45: ; Start the list again. .55:ListGetNext EAX JNZ .50: .57: ; Step 4 - the actual ordering employs macros OrderSegment and OrderGroup. OrderGroup %MACRO Purpose=sssPurposeAny, NamePtr=0, NameSize=0 %IF "%Purpose" !== "EDX" MOV EDX,%Purpose %ENDIF MOV ESI,%NamePtr MOV ECX,%NameSize CALL PgmOrderSegments.OrderGroup: %ENDMACRO OrderGroup OrderSegment %MACRO Purpose=sssPurposeAny, NamePtr=0, NameSize=0, Group=-1 %IF "%Purpose" !== "EDX" MOV EDX,%Purpose %ENDIF MOV ESI,%NamePtr MOV ECX,%NameSize %IF "%Group" !== "EDI" MOV EDI,%Group %ENDIF CALL PgmOrderSegments.OrderSegment: %ENDMACRO OrderSegment ; The actual group&segment order. .G1:OrderGroup NamePtr= =B'COMGROUP',NameSize=8 JZ .C5: MOV EDI,EAX .G3:OrderSegment Purpose=sssPurposePHDR,Group=EDI ; Pseudosegment [PSP] (used in COM). .G4:OrderSegment Purpose=sssPurposeCODE,Group=EDI JNZ .G4: .G5:OrderSegment Purpose=sssPurposeRODATA,Group=EDI JNZ .G5: .G6:OrderSegment Purpose=sssPurposeDATA,Group=EDI JNZ .G6: .G7:OrderSegment Purpose=sssPurposeBSS,Group=EDI JNZ .G7: .G8:OrderSegment Purpose=sssPurposeSTACK,Group=EDI JNZ .G8: .C5:OrderGroup Purpose=sssPurposeCODE JZ .C9: MOV EDI,EAX .C6:OrderSegment Purpose=sssPurposeCODE,Group=EDI JNZ .C6: JMP .C5: ; The next code group. .C9:OrderSegment Purpose=sssPurposeCODE ; Ungrouped code segments. JNZ .C9: .R5:OrderGroup Purpose=sssPurposeRODATA ; Monodata memory models may group rodada, data and bss segments. JZ .R9: MOV EDI,EAX .R6:OrderSegment Purpose=sssPurposeRODATA,Group=EDI JNZ .R6: .R7:OrderSegment Purpose=sssPurposeDATA,Group=EDI JNZ .R7: .R8:OrderSegment Purpose=sssPurposeBSS,Group=EDI JNZ .R8: JMP .R5: ; The next rodata group. .R9:OrderSegment Purpose=sssPurposeRODATA ; Ungrouped rodata segments. JNZ .R9: .D5:OrderGroup Purpose=sssPurposeDATA JZ .D9: MOV EDI,EAX .D6:OrderSegment Purpose=sssPurposeRODATA,Group=EDI ; Monodata memory models may group rodada, data and bss segments. JNZ .D6: .D7:OrderSegment Purpose=sssPurposeDATA,Group=EDI JNZ .D7: .D8:OrderSegment Purpose=sssPurposeBSS,Group=EDI JNZ .D8: JMP .D5: ; The next data group. .D9:OrderSegment Purpose=sssPurposeDATA ; Ungrouped data segments. JNZ .D9: .B5:OrderGroup Purpose=sssPurposeBSS JZ .B9: MOV EDI,EAX .B6:OrderSegment Purpose=sssPurposeRODATA,Group=EDI ; Monodata memory models may group rodada, data and bss segments. JNZ .B6: .B7:OrderSegment Purpose=sssPurposeDATA,Group=EDI JNZ .B7: .B8:OrderSegment Purpose=sssPurposeBSS,Group=EDI JNZ .B8: JMP .B5: ; The next bss group. .B9:OrderSegment Purpose=sssPurposeBSS ; Ungrouped bss segments. JNZ .B9: .S1:OrderGroup Purpose=sssPurposeSTACK JZ .S5: MOV EDI,EAX .S2:OrderSegment Purpose=EDX,Group=EDI JNZ .S2: JMP .S1: ; The next stack group. .S5:OrderSegment Purpose=sssPurposeSTACK ; Ungrouped stack segments. JNZ .S5: OrderSegment sssPurposeEXPORT ; Order PE-special segment [.edata]. OrderSegment sssPurposeIMPORT ; Order PE-special segment [.idata]. OrderSegment sssPurposeRESOURCE ; Order PE-special segment [.rsrc]. OrderSegment sssPurposeBASERELOC ; Order PE-special segment [.reloc]. OrderSegment ; Any other remaining segments. ; Alphabetically reorder segments from the same group with identical purposes. BufferRetrieve [EBX+PGM.SegOrdBuffer] ADD ECX,ESI ; ESI..ECX is an array of DWORD pointers to segments (perhaps in wrong order). MOV [%ArrayPtr],ESI MOV [%ArrayEnd],ECX .60:MOV ESI,[%ArrayPtr] ; Find a group. CMP ESI,[%ArrayEnd] JNB .99: LODSD MOV [%ArrayPtr],ESI .65:JNSt [EAX+SSS.Status],sssGroup,.60: MOV EBX,EAX ; EBX is pointer to a group. Pointers to its segments may follow. .70:MOV ESI,[%ArrayPtr] CMP ESI,[%ArrayEnd] JNB .60: MOV [%Ptr1],ESI LODSD MOV [%ArrayPtr],ESI JNSt [EAX+SSS.Status],sssSegment,.65: CMP [EAX+SSS.GroupPtr],EBX JNE .60: MOV EDX,[EAX+SSS.Purpose] .75: ; Find more segments with identical group EBX and purpose EDX. MOV ESI,[%ArrayPtr] CMP ESI,[%ArrayEnd] JNB .85: LODSD MOV [%ArrayPtr],ESI JNSt [EAX+SSS.Status],sssSegment,.80: CMP [EAX+SSS.GroupPtr],EBX JNE .80: CMP [EAX+SSS.Purpose],EDX JE .75: .80:MOV ESI,[%ArrayPtr] ; The just loaded object at %ArrayPtr is not a suitable segment. Roll back. SUB ESI,4 MOV [%ArrayPtr],ESI .85:MOV EDX,[%ArrayPtr] SUB EDX,[%Ptr1] ; Array [%Ptr1] .. [%ArrayPtr] contains pointers to groupen segments with identical purpose. CMP EDX,4 JNA .70: ; Skip when nothing to sort. SHR EDX,2 ; EDX is number of segments to sort. ; If at least one segment has $ in its name, sort all of them alphabetically. MOV ESI,[%Ptr1] .90:CMP ESI,[%ArrayPtr] JNB .70: LODSD MOV EDI,[EAX+SSS.NamePtr] MOV ECX,[EAX+SSS.NameSize] MOV AL,'$' REPNE SCASB JNE .90: ; No '$' in segment name, try the next segment. ShellSort [%Ptr1],EDX,4,.ByName JMP .70: .ByName: PROC ; Sort callback procedure. Input: ESI and EDI points to DWORD ^SSS. PUSH ECX,EBX,EBP MOV EBX,[ESI] MOV EBP,[EDI] MOV ECX,[EBX+SSS.NameSize] MOV EDX,[EBP+SSS.NameSize] CMP ECX,EDX JBE .N2: MOV ECX,EDX .N2: PUSH ESI,EDI MOV ESI,[EBX+SSS.NamePtr] MOV EDI,[EBP+SSS.NamePtr] REPE CMPSB ; Compare shorter part of segment names. POP EDI,ESI JA .Swap: JB .Ord: CMP EDX,[EBP+SSS.NameSize] ; When they match, compare lengths. JB .Swap: .Ord: CLC ; Both segments EBX,EBP are in order. JMPS .N9: .Swap:MOV ECX,[EBX+SSS.SegmIndex] ; Swap pointers at ESI,EDI and their segment indexes. MOV EDX,[EBP+SSS.SegmIndex] MOV [EBX+SSS.SegmIndex],EDX MOV [EBP+SSS.SegmIndex],ECX MOV [ESI],EBP MOV [EDI],EBX STC ; Signalize that they were swapped. .N9:POP EBP,EBX,ECX RET ENDP .ByName: .OrderGroup: PROC1 ; Runtime procedure of macro OrderGroup. ; Input: EDX=purpose, ESI=^name, ECX=SIZE# name, EBX=^PGM. ; Output: ZF=0, EAX=^SSS, EAX stored to PGM.SegOrdBuffer. ; Error: ZF=1, EAX=0 when the group was not found. ListGetFirst [EBX+PGM.SssList] .G1:JSt [EAX+SSS.Status],sssOrdered,.G7: JNSt [EAX+SSS.Status],sssGroup,.G7: JNSt [EAX+SSS.Purpose],EDX,.G7: TEST ESI ; ESI=0 allows any name. JZ .G8: CMP ECX,[EAX+SSS.NameSize] JNE .G7: JECXZ .G8: PUSH ECX,ESI,EDI MOV EDI,[EAX+SSS.NamePtr] REPE CMPSB POP EDI,ESI,ECX JE .G8: .G7:ListGetNext EAX ; Try the next group. JNZ .G1: RET ; ZF=1, EAX=0, not found. .G8:BufferStoreDword [EBX+PGM.SegOrdBuffer],EAX ; Store group EAX. SetSt [EAX+SSS.Status],sssOrdered RET ; ZF=0, EAX=^SSS, found. ENDP1 .OrderGroup .OrderSegment: PROC1 ; Runtime procedure of macro OrderSegment. ; Input: EDX=purpose, ESI=^name, ECX=SIZE# name, EDI=^SSS group, EBX=^PGM. ; Output: ZF=0, EAX=^SSS, EAX stored to PGM.SegOrdBuffer. ; Error: ZF=1, EAX=0 when the segment was not found. ListGetFirst [EBX+PGM.SssList] .S1:JSt [EAX+SSS.Status],sssOrdered,.S7: JNSt [EAX+SSS.Status],sssSegment,.S7: JNSt [EAX+SSS.Purpose],EDX,.S7: CMP EDI,-1 ; Any group membership? JE .S5: CMP [EAX+SSS.GroupPtr],EDI JNE .S7: .S5:TEST ESI ; ESI=0 allows any name. JZ .S8: CMP ECX,[EAX+SSS.NameSize] JNE .S7: JECXZ .S8: PUSH ECX,ESI,EDI MOV EDI,[EAX+SSS.NamePtr] REPE CMPSB POP EDI,ESI,ECX JE .S8: .S7:ListGetNext EAX ; Try the next segment. JNZ .S1: RET ; ZF=1, EAX=0, not found. .S8:BufferStoreDword [EBX+PGM.SegOrdBuffer],EAX ; Order segment EAX. SetSt [EAX+SSS.Status],sssOrdered INCD [%SegmIndex] PUSHD [%SegmIndex] POPD [EAX+SSS.SegmIndex] TEST EAX RET ; ZF=0, EAX=^SSS, found. ENDP1 .OrderSegment: .99:EndProcedure PgmOrderSegments
ENDPROGRAM pgm