This source generates EuroAssembler output object file in program format MZ (16bit or 32bit DOS executable program).
pfmz PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
pfmz HEAD ; Start module interface.
PFMZ_DOS_HEADER STRUC ; DOS .EXE header .e_magic D WORD ; Magic number "MZ". .e_cblp D WORD ; Bytes on the last page of MZ file (0..511). .e_cp D WORD ; 512-byte pages in MZ file, including the incomplete last one. .e_crlc D WORD ; Number of relocations. .e_cparhdr D WORD ; Size of header in 16B paragraphs (PFMZ_DOS_HEADER + relocations). .e_minalloc D WORD ; Minimum extra paragraphs needed (trailing BSS sections). .e_maxalloc D WORD ; Maximum extra paragraphs needed. .e_ss D WORD ; Initial (image relative) SS value. .e_sp D WORD ; Initial SP value. .e_csum D WORD ; Word checksum of the whole file, or 0. .e_ip D WORD ; Initial IP value. .e_cs D WORD ; Initial (image relative) CS value. .e_lfarlc D WORD ; File offset of relocation table (usually 64). .e_ovno D WORD ; Overlay number, not supported in €ASM (0). .e_res D 4*WORD ; Reserved. .e_oemid D WORD ; OEM identifier (for e_oeminfo). .e_oeminfo D WORD ; OEM information (e_oemid specific). .e_res2 D 10*WORD ; Reserved. .e_lfanew D DWORD ; File offset of PE, LE or NE header. ENDSTRUC PFMZ_DOS_HEADER ; SIZE# PFMZ_DOS_HEADER = 64.
ENDHEAD pfmz ; End of module interface.
PfmzCompile Procedure OutputStream, Pgm
MzDosHeader LocalVar Size=SIZE# PFMZ_DOS_HEADER
MzRelocBuffer LocalVar ; Buffer with 16+16bit far pointers to image base relocations.
MzImageBuffer LocalVar ; Buffer with raw segments contents.
TopInitRVA LocalVar ; Highest RVA of initialized segments. ImageSize=TopInitRVA-0.
TopUninitRVA LocalVar ; Highest RVA of uninitialized segments. BssSize=TopUninitRVA-TopInitRVA.
MOV EBX,[%Pgm]
Invoke EaBufferReserve::,PfmzCompile
MOV [%MzRelocBuffer],EAX
Invoke PgmCreateImplicitGroups::,EBX
Invoke PfmzDefaultStack::,EBX
Invoke RelocSort::,EBX
Invoke PgmOrderSegments::,EBX
Invoke PgmLinkImage::,EBX,0
; Store unresolved relocations to %MzRelocBuffer.
BufferRetrieve [EBX+PGM.SssPtrBuf] ; Pointers to segments.
SHR ECX,2
JZ .25: ; If no segments.
.10:LODSD
JNSt [EAX+SSS.Status],sssSegment,.22:
MOV EBX,EAX ; Segment whose word is relocated.
PUSH ECX,ESI
BufferRetrieve [EBX+SSS.RelocBuffer]
JECXZ .19: ; If no relocations in segment EBX.
.13: PUSH ESI
JSt [ESI+RELOC.Status],relocResolved|relocDisp8,.16:
JNSt [ESI+RELOC.Status],relocPara,.16: ; Only paragraph relocations are left to resolve at load time.
MOV EAX,[EBX+SSS.BottomLow]
MOV EDX,0x0000_000F
AND EDX,EAX ;
SHL EAX,12 ; Convert RVA to paragraph address and move it to the upper half of EAX.
ADD EDX,[ESI+RELOC.OrgLow]
MOV AX,DX
BufferStoreDword [%MzRelocBuffer],EAX ; Store MZ relocation far pointer.
.16: POP ESI
ADD ESI,SIZE#RELOC
SUB ECX,SIZE#RELOC
JA .13:
.19:POP ESI,ECX
.22:LOOP .10: ; Next segment.
.25:;Create raw image.
MOV EBX,[%Pgm]
Invoke PgmConcatenateImage::,EBX
MOV [%MzImageBuffer],EAX
;Find image RVA.
BufferRetrieve [EBX+PGM.SssPtrBuf]
LEA EDI,[ESI+ECX-4] ; EDI points to the pointer to the last linked segment (usually uninitialized).
MOV EDX,[EDI]
MOV EAX,[EDX+SSS.TopLow]
MOV [%TopUninitRVA],EAX ; RVA of image top, including BSS+STACK segments.
MOV ECX,[EBX+PGM.NrOfInitSegments]
LEA EDI,[ESI+4*ECX-4] ; EDI points to the pointer to the last initialized segment in image.
SUB EAX,EAX
CMP EDI,ESI
JB .28: ; If no initialized data in image.
MOV EDX,[EDI] ; EDX points to the last initialized segment.
MOV EAX,[EDX+SSS.TopLow]
.28:MOV [%TopInitRVA],EAX
; Construct MzDosHeader.
LEA EDI,[%MzDosHeader]
MOV ECX,SIZE# PFMZ_DOS_HEADER
Clear EDI,Size=ECX
MOVW [EDI+PFMZ_DOS_HEADER.e_magic],"MZ"
MOV [EDI+PFMZ_DOS_HEADER.e_lfarlc],CX ; Fileoffset of relocation table.
BufferRetrieve [%MzRelocBuffer] ; Its size is not YWORD aligned yet.
MOV EAX,ECX
SHR ECX,2
MOV [EDI+PFMZ_DOS_HEADER.e_crlc],CX ; Number of load-time relocations.
SHR ECX,16
Msg cc=NZ,'7923',EAX ; Number of relocations (!1D) exceeded 64K.
Invoke EaBufferAlign::,[%MzRelocBuffer],32 ; Round up the header size to even paragraphs (YWORD).
BufferRetrieve [%MzRelocBuffer]
ADD ECX,SIZE# PFMZ_DOS_HEADER
MOV EDX,ECX
SHR ECX,4 ; Convert header size to 16B paragraphs.
MOV [EDI+PFMZ_DOS_HEADER.e_cparhdr],CX ; Size of header (including the relocation table).
BufferRetrieve [%MzImageBuffer]
ADD ECX,EDX ; ECX is now the total file size.
MOV EAX,ECX
AND AX,0x01FF
MOV [EDI+PFMZ_DOS_HEADER.e_cblp],AX ; Bytes on the last page.
ADD ECX,0x01FF
SHR ECX,9
MOV [EDI+PFMZ_DOS_HEADER.e_cp],CX ; Number of pages in the file.
MOV EAX,[%TopUninitRVA]
SUB EAX,[%TopInitRVA]
ADD EAX,15
SAR EAX,4 ; Convert reserved size to paragraphs.
MOV [EDI+PFMZ_DOS_HEADER.e_minalloc],AX ; Minimal BSS allocation required to execute.
MOV EAX,[EBX+PGM.Pgmopt.SizeOfHeapCommitLow]
SAR EAX,4
CMP EAX,0xFFFF
JBE .31:
MOV EAX,0xFFFF
.31:MOV [EDI+PFMZ_DOS_HEADER.e_maxalloc],AX ; Suggested maximal memory allocation for the image.
; Find the last STACK segment to EDX.
XOR EDX,EDX
BufferRetrieve [EBX+PGM.SssPtrBuf]
SHR ECX,2
JZ .40:
.34:LODSD
JNSt [EAX+SSS.Status],sssSegment,.37:
JNSt [EAX+SSS.Purpose],sssPurposeSTACK,.37:
MOV EDX,EAX
.37:LOOP .34:
.40:TEST EDX
Msg cc=Z,'3922' ; Missing segment with PURPOSE=STACK.
JZ .43:
MOV EAX,[EDX+SSS.BottomLow]
MOV ECX,EAX
SHR EAX,4
MOV [EDI+PFMZ_DOS_HEADER.e_ss],AX
MOV EAX,[EDX+SSS.TopLow]
SUB EAX,ECX
MOV [EDI+PFMZ_DOS_HEADER.e_sp],AX
.43: ; Reevaluate entry point which may refer to unresolved external symbol.
Invoke PgmEvalEntry::,EBX
LEA EDX,[EBX+PGM.EntryExp]
.46:MOV ECX,[EDX+EXP.Status]
LEA EAX,[EBX+PGM.Pgmopt.EntryPtr]
Dispatch CL,'A','N','F',0
Msg '7711',EAX ; Invalid program entry point "!1S".
JMP .N:
.0: Msg '7710' ; Missing program entry point.
JMP .N:
.F: ; Entry EDX is specified as immediate far pointer, e.g. ENTRY=0x0040:1234h.
Msg '2921',EAX ; Nonrelocable entry point "!1S" is not supported by linker.
MOV ECX,[EDX+EXP.Seg]
MOV EAX,[EDX+EXP.Low]
JMP .64:
.N: ; Entry EDX is specified as absolute offset (no segment), e.g. ENTRY=100h.
; Assume it is the offset in the first code segment.
BufferRetrieve [EBX+PGM.SssPtrBuf]
JECXZ .58:
.49:LODSD
JNSt [EAX+SSS.Status],sssSegment,.52:
JSt [EAX+SSS.Purpose],sssPurposeCODE,.55:
.52:LOOP .49:
JMP .58:
.55:MOV ECX,[EAX+SSS.BottomLow]
.58:MOV EAX,[EDX+EXP.Low]
JMP .64:
.A: ; Entry EDX is specified as an address symbol, e.g. ENTRY=Main.
; It was already relocated withing image or it may be external.
MOV ESI,[EDX+EXP.Seg]
JNSt [ESI+SSS.Status],sssExtern,.60:
; ESI is extern pseudosegment of entry in linked module.
MOV EAX,[EDX+EXP.Low]
MOV ECX,[ESI+SSS.SymPtr]
JECXZ .60:
ADD EAX,[ECX+SYM.OffsetLow] ; ECX is matched public symbol.
MOV ECX,[ECX+SYM.Section]
JECXZ .60:
MOV ECX,[ECX+SSS.SegmPtr] ; Segment in base program.
JECXZ .60:
ADD EAX,[ECX+SSS.BottomLow]
MOV ECX,[ECX+SSS.GroupPtr] ; Group in linked program.
JECXZ .60:
MOV EDX,[ECX+SSS.GroupPtr] ; Group in base program.
MOV ECX,[EDX+SSS.BottomLow]
SUB EAX,ECX ; EAX is now entry offset relative to base group.
JMP .64:
.60:PUSH EDX ; Temporary save pointer to entry expression.
MOV EBX,ESI
MOV ECX,[ESI+SSS.GroupPtr]
JECXZ .61:
MOV ESI,ECX
.61: ; ESI is now frame (code group or ungrouped segment). EBX is segment.
MOV EAX,[EBX+SSS.BottomLow]
MOV EDX,[EBX+SSS.BottomHigh]
SUB EAX,[ESI+SSS.BottomLow]
SBB EDX,[ESI+SSS.BottomHigh]
POP EDX
ADD EAX,[EDX+EXP.Low]
ADC EDX,[EDX+EXP.High]
MOV ECX,[ESI+SSS.BottomLow]
.64: ; ECX is RVA of entry group, EAX is entry offset.
MOV EDX,0x0000_000F
AND EDX,ECX ; Nonzero when the group is not paragraph aligned.
ADD EAX,EDX
SHR ECX,4
MOV [EDI+PFMZ_DOS_HEADER.e_ip],AX
MOV [EDI+PFMZ_DOS_HEADER.e_cs],CX
; Calculate file checksum.
XOR EDX,EDX ; Checksum word accumulator.
LEA ESI,[%MzDosHeader]
MOV ECX,SIZE# PFMZ_DOS_HEADER /2
.67:LODSW
ADD EDX,EAX
LOOP .67:
BufferRetrieve [%MzRelocBuffer]
SHR ECX,1
JZ .73:
.70:LODSW
ADD EDX,EAX
LOOP .70:
.73:BufferRetrieve [%MzImageBuffer]
SUB EBX,EBX
JECXZ .82:
SHR ECX,1
JNC .76:
INC EBX ; Round up file size to even.
BufferStoreByte [%MzImageBuffer],0 ; Stuff the file before calculating checksum.
.76:BufferRetrieve [%MzImageBuffer]
SHR ECX,1
.79:LODSW
ADD EDX,EAX
LOOP .79:
BufferDecrement [%MzImageBuffer],Size=EBX ; Remove temporary stuff NULL byte if EBX=1.
.82:NOT EDX
MOV [EDI+PFMZ_DOS_HEADER.e_csum],DX
; File is completly compiled in buffers.
MOV EBX,[%OutputStream]
LEA ESI,[%MzDosHeader]
StreamStore EBX,ESI,SIZE#PFMZ_DOS_HEADER
BufferRetrieve [%MzRelocBuffer]
StreamStore EBX,ESI,ECX
BufferRetrieve [%MzImageBuffer]
StreamStore EBX,ESI,ECX
Invoke EaBufferRelease::,[%MzRelocBuffer]
.90:EndProcedure PfmzCompile
PROGRAM SIZEOFSTACKCOMMIT=
(factory-default is 4KB), when all of theese conditions are met:
PfmzDefaultStack Procedure Pgm
MOV EBX,[%Pgm]
MOV EAX,[EBX+PGM.Pgmopt.Status]
AND EAX,pgmoptFormatMask
CMP AL,pgmoptMZ
JNE .90:
JNSt [Ea::+EA.Eaopt+EAOPT.Status],eaoptAUTOSEGMENT,.90:
Invoke SssFind::,sssSegment,0,=B"STACK",5,EBX
JNC .90:
MOV EDX,[EBX+PGM.SssList]
ListGetLast EDX
JZ .50:
.10:JNSt [EAX+SSS.Status],sssSegment,.40:
JSt [EAX+SSS.Purpose],sssPurposeSTACK,.90:
.40:ListGetPrev EAX
JNZ .10:
.50: ; No stack segment found. Implicit [STACK] will be created.
Invoke SssCreate::,[EBX+PGM.CurrentStm],0,=B"STACK",5, \
sssSegment+sssStack+sssImplicit+sssUsed,sssPurposeSTACK, 16
JSt [EAX+SSS.Status],sssWidth32 | sssWidth64, .60:
SetSt [EAX+SSS.Status],sssWidth16 ; If the width wasn't specified.
.60:MOV ECX,[EBX+PGM.Pgmopt.SizeOfStackCommitLow]
MOV [EAX+SSS.TopLow],ECX
.90:EndProcedure PfmzDefaultStack
PfmzLoadPgm Procedure BasePgm, ObjBegin, ObjSize, FileNamePtr
Msg '8534',Dict_FormatMZ::,[%FileNamePtr] ; Format !1S of file "!2$" is not linkable.
EndProcedure PfmzLoadPgm
ENDPROGRAM pfmz