This source generates EuroAssembler output object file in program format MZ (16bit or 32bit DOS executable program).
EUROASM NOWARN=2101
pfmz PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
INCLUDEHEAD euroasm.htm, \ Interface (structures, symbols and macros) of other modules used in this source.
dict.htm,ea.htm,eaopt.htm,exp.htm,msg.htm,pf.htm,pgm.htm,pgmopt.htm,reloc.htm,sss.htm,stm.htm,sym.htm
pfmz HEAD ; Start module interface.
PFMZ_DOS_HEADER STRUC ; DOS .EXE header
; +00h.
.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 16-byte 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.
; +10h.
.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 2*WORD ; Reserved.
; +20h.
D 2*WORD ; Reserved.
.e_oemid D WORD ; OEM identifier (for e_oeminfo).
.e_oeminfo D WORD ; OEM information (e_oemid specific).
.e_res2 D 4*WORD ; Reserved.
; +30h.
D 6*WORD ; Reserved.
.e_lfanew D DWORD ; File offset of PE, LE or NE header.
ENDSTRUC PFMZ_DOS_HEADER ; SIZE# PFMZ_DOS_HEADER = 40h = 64.
ENDHEAD pfmz ; End of module interface.
PfmzCompile Procedure OutputStream, Pgm
ParaRelocBuffer LocalVar ; ^BUFFER for DWORD pointers to RELOC records with relocPara|relocFar.
MzRelocBuffer LocalVar ; ^BUFFER for 16+16bit far pointers to the image base relocations.
StubSize LocalVar ; Size of MZ header + MZ relocations, aligned to 32 bytes.
EmittedSize LocalVar ; Total size of emitted image (CODE+RODATA+DATA).
AllocatedSize LocalVar ; Total size of allocated image (%EmittedSize+BSS+STACK).
MzDosHeader LocalVar Size=SIZE# PFMZ_DOS_HEADER ; Room for the MZ header (64 bytes).
ClearLocalVar
Invoke EaBufferReserve::,%^PROC
MOV [%ParaRelocBuffer],EAX
Invoke EaBufferReserve::,%^PROC
MOV [%MzRelocBuffer],EAX
MOV EBX,[%Pgm]
Invoke PfmzDefaultStack::,EBX
Invoke SymReportUnresolved::,EBX
Invoke PgmGroupByModel::,EBX
Invoke PgmOrderSegments::,EBX ; Groups and segments will be ordered in Pgm.SegOrdBuffer.
; Copy pointers to unresolved paragraph relocations from all segments to %ParaRelocBuffer.
BufferRetrieve [EBX+PGM.SegOrdBuffer] ; Pointers to segments in image order.
SHR ECX,2
JZ .32:
.22:LODSD
JNSt [EAX+SSS.Status],sssSegment,.30:
MOV EDI,EAX ; Segment whose words are relocated.
PUSH ECX,ESI
BufferRetrieve [EDI+SSS.RelocBuffer]
JECXZ .28: ; If there are no relocations in segment EDI.
.24: MOV EAX,[ESI+RELOC.Status]
JSt EAX,relocIgnore,.26:
JSt EAX,relocPara|relocFar,.25:
JNSt EAX,relocAbsVA,.26:
MOV EDX,relocExtAttr
AND EDX,EAX
CMP EDX,dictAttrPARA<<16 ; >>
JNE .26:
RstSt EAX,relocAbsVA
SetSt EAX,relocPara
MOV [ESI+RELOC.Status],EAX
.25: BufferStoreDword [%ParaRelocBuffer],ESI ; Pointer to relocations which requires resolving at load time.
.26: ADD ESI,SIZE# RELOC
SUB ECX,SIZE# RELOC
JA .24: ; The next relocation.
.28:POP ESI,ECX
.30:LOOP .22: ; The next segment.
.32:BufferRetrieve [%ParaRelocBuffer]
MOV EDX,ECX
SHR ECX,2
CMP ECX,64K ; Number of relocations.
Msg cc=NB,'7923',ECX ; Number of relocations (!1D) exceeded 64K.
ADD EDX,1Fh ; Each MZ relocation is FAR PTR (4 bytes).
AND EDX,~1Fh ; Align MZ relocation block to even paragraphs (32 bytes).
ADD EDX,SIZE# PFMZ_DOS_HEADER ; Compute StubSize (MZ header + MZ relocations).
MOV [%StubSize],EDX
Invoke PgmLink::,EBX,EDX,0
Invoke PgmRelocResolve::,EBX
; Preformat MzDosHeader.
LEA EDI,[%MzDosHeader]
MOVW [EDI+PFMZ_DOS_HEADER.e_magic],"MZ"
MOVW [EDI+PFMZ_DOS_HEADER.e_lfarlc],SIZE# PFMZ_DOS_HEADER
BufferRetrieve [%ParaRelocBuffer] ; Count MZ relocations.
SHR ECX,2
MOV [EDI+PFMZ_DOS_HEADER.e_crlc],CX ; Number of relocations.
SHR EDX,4 ; Convert the header (stub) size to 16-bytes paragraphs.
MOV [EDI+PFMZ_DOS_HEADER.e_cparhdr],DX ; Header size (including the relocation table).
; Compute emitted image size and virtual size.
BufferRetrieve [EBX+PGM.SegOrdBuffer]
ADD ECX,ESI
.36:CMP ESI,ECX
JNB .37:
LODSD
JNSt [EAX+SSS.Status],sssSegment,.36:
MOV EDX,[EAX+SSS.TopLow]
MOV [%AllocatedSize],EDX
JNSt [EAX+SSS.Status],sssNotBSS,.36:
MOV [%EmittedSize],EDX ; Segment EAX is initialized.
JMP .36:
.37:MOV EAX,[%StubSize]
ADD [%AllocatedSize],EAX
ADD [%EmittedSize],EAX
; Update MzDosHeader.
.46:MOV EAX,[%AllocatedSize]
SUB EAX,[%EmittedSize]
ADD EAX,15
SAR EAX,4 ; Convert the reserved size to paragraphs.
MOV [EDI+PFMZ_DOS_HEADER.e_minalloc],AX ; Minimal BSS allocation required to execute.
AND EAX,0xFFFF_0000
Msg cc=NZ,'8521' ; MZ image size exceeded 1 MB.
MOV EAX,[%EmittedSize]
MOV ECX,EAX ; EAX and ECX is now the total file size.
AND EAX,0x01FF
MOV [EDI+PFMZ_DOS_HEADER.e_cblp],AX ; Number of 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,[EBX+PGM.Pgmopt.SizeOfHeapCommitLow] ; Suggested maximal memory allocation size for the image (in bytes).
MOV EDX,0xFFFF ; Maximal possible value in paragraphs for realmode programs.
SAR EAX,4 ; Convert the size to paragraphs.
CMP EAX,EDX
JBE .48:
XCHG EAX,EDX ; Maximal possible value for realmode programs.
.48:MOV [EDI+PFMZ_DOS_HEADER.e_maxalloc],AX
; Find STACK group or segment to EDX.
XOR EDX,EDX
BufferRetrieve [EBX+PGM.SegOrdBuffer]
SHR ECX,2
JZ .W3922: ; Missing segment with PURPOSE=STACK.
.50:LODSD
JNSt [EAX+SSS.Status],sssGroup,.52:
JNSt [EAX+SSS.Purpose],sssPurposeSTACK,.52:
MOV EDX,EAX
.52:LOOP .50:
TEST EDX
JNZ .58:
BufferRetrieve [EBX+PGM.SegOrdBuffer]
SHR ECX,2
.54:LODSD
JNSt [EAX+SSS.Status],sssSegment,.56:
JNSt [EAX+SSS.Purpose],sssPurposeSTACK,.56:
MOV EDX,EAX
.56:LOOP .54:
TEST EDX
JZ .W3922: ; Missing segment with PURPOSE=STACK.
.58:MOV EAX,[EDX+SSS.BottomLow]
MOV ECX,[EDX+SSS.TopLow]
TEST EAX,0xFFF0_0000
Msg cc=NZ,'7714' ; Stack above 1 MB is not encodable in this program format.
TEST EAX,0x0000_000F
Msg cc=NZ,'3843',EDX ; Stack segment [!1S] is not paragraph aligned.
AND EAX,0x000F_FFF0
SUB ECX,EAX
SHR EAX,4
MOV [EDI+PFMZ_DOS_HEADER.e_ss],AX ; Initial SS: RVA paragraph address of the stack bottom.
MOV [EDI+PFMZ_DOS_HEADER.e_sp],CX ; Initial SP: size of the stack segment.
JMP .60:
.W3922:Msg '3922' ; Missing segment with PURPOSE=STACK.
.60: ; Entry point which may refer to unresolved external symbol.
Invoke PgmEvalEntry::,EBX
LEA EDX,[EBX+PGM.EntryExp]
MOV ECX,[EDX+EXP.Status]
LEA EAX,[EBX+PGM.Pgmopt.EntryPtr] ; ENTRY= value in case of E7711.
Dispatch CL,'A','N','F',00
Msg '7711',EAX ; Invalid program entry point "!1S".
; Simulate missing or invalid entry as if it were scalar 0.
.N: ; EntryExp EDX is specified as an absolute offset (no segment), e.g. ENTRY=100h.
; Assume it is the offset in the first code segment.
BufferRetrieve [EBX+PGM.SegOrdBuffer]
JECXZ .F:
.62:LODSD ; Search for a code segment.
JNSt [EAX+SSS.Status],sssSegment,.64:
JSt [EAX+SSS.Purpose],sssPurposeCODE,.6B:
.64:LOOP .62:
.F: ; EntryExp EDX was specified as immediate far pointer, e.g. ENTRY=0x0040:1234h.
Msg '2921',EAX ; Nonrelocable entry point "!1S" is not supported.
MOV ECX,[EDX+EXP.Seg] ; Paragraph address of the entry.
MOV EAX,[EDX+EXP.Low] ; Offset of the entry.
JMP .73:
.00:Msg '7710' ; Missing program entry point.
JMP .N:
.E6961:Msg '6961',ECX ; Unresolved external/imported symbol "!1S".
JMP .N:
.A: ; Entry expression EDX was evaluated as an address symbol or expression, e.g. ENTRY=Main+4.
MOV ECX,[EDX+EXP.Sym]
JECXZ .N:
JNSt [ECX+SYM.Status],symExtern|symImport,.69:
JNSt [ECX+SYM.Status],symResolved,.E6961: ; Unresolved external/imported symbol "!1S".
MOV ECX,[ECX+SYM.SymbPtr] ; ECX=resolved entry symbol.
JECXZ .E6961: ; Unresolved external/imported symbol "!1S".
.69:MOV EAX,[ECX+SYM.Section]
TEST EAX
JZ .E6961: ; Unresolved external/imported symbol "!1S".
.6B:MOV ECX,[EAX+SSS.SVA]
ADD ECX,[EDX+EXP.Low] ; ECX is now the linear RVA of the entry.
MOVZX EAX,CX ; EAX is offset part of the entry point.
SHR ECX,16
SHL ECX,12 ; ECX is paragraph addres of the entry point.
.73:TEST ECX,0xFFFF_0000
Msg cc=NZ,'7715' ; Entry above 1 MB is not encodable in this program format.
.74:MOV [EDI+PFMZ_DOS_HEADER.e_ip],AX
MOV [EDI+PFMZ_DOS_HEADER.e_cs],CX
; Resolve and convert paragraph relocations (relocPara) to 16+16 FAR pointers in MZRelocBuffer.
BufferRetrieve [%ParaRelocBuffer] ; Buffer with pointers to relocPara records.
SHR ECX,2
JZ .78:
.76:LODSD
PUSH ESI
MOV ESI,EAX
SetSt [ESI+RELOC.Status],relocResolved
MOV EDI,[ESI+RELOC.Section]
MOV EAX,[EDI+SSS.BottomLow]
TEST EAX,0xFFF0_0000
Msg cc=NZ,'7716',[ESI+RELOC.Section],[ESI+RELOC.OrgLow] ; Relocation [!1S]:!2H above 1 MB is not encodable in this program format.
MOV EDX,0x0000_000F
AND EDX,EAX
ADD EDX,[ESI+RELOC.OrgLow]
SHL EAX,12
MOV AX,DX
BufferStoreDword [%MzRelocBuffer],EAX ; Store MZ relocation far pointer.
.77:POP ESI
LOOP .76:
.78:Invoke EaBufferAlign::,[%MzRelocBuffer],32 ; Round up to even paragraphs (YWORD).
Invoke RelocReportUnresolved::,EBX
; Calculate file checksum.
XOR EDX,EDX ; Checksum word accumulator.
LEA ESI,[%MzDosHeader]
MOV ECX,SIZE# PFMZ_DOS_HEADER /2
.80:LODSW
ADD EDX,EAX
LOOP .80:
BufferRetrieve [%MzRelocBuffer]
SHR ECX,1
JZ .84:
.82:LODSW
ADD EDX,EAX
LOOP .82:
.84:MOV EDI,[%OutputStream]
StreamGetSize EDI ; Return number of bytes in EAX.
StreamStoreByte EDI,0 ; Temporarily word-align the stream size in case it was odd.
.86:StreamRetrieve EDI ; Get one stream block to ESI,ECX.
SHR ECX,1
JZ .90: ; No more data in stream. EDX is the word checksum.
.88:LODSW
ADD EDX,EAX ; Compute checksum.
LOOP .88:
JMP .86: ; The next stream block.
.90:NOT EDX
LEA EBX,[%MzDosHeader]
MOV [EBX+PFMZ_DOS_HEADER.e_csum],DX
; File is completly compiled in buffers.
StreamClear EDI
StreamStore EDI,EBX,SIZE# PFMZ_DOS_HEADER
BufferRetrieve [%MzRelocBuffer]
StreamStore EDI,ESI,ECX
Invoke PgmStreamImage::,[%Pgm],EDI
Invoke EaBufferRelease::,[%MzRelocBuffer]
Invoke EaBufferRelease::,[%ParaRelocBuffer]
.99:EndProcedure PfmzCompile
[STACK] with size
specified by the option PROGRAM SIZEOFSTACKCOMMIT=
(factory-default is 8 KB), when these 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.Eaopt.Status::],eaoptAUTOSEGMENT,.90:
Invoke SssFindByName::,sssSegment,0,=B"STACK",5,EBX
JNC .60:
ListGetLast [EBX+PGM.SssList]
JZ .50:
.10:JNSt [EAX+SSS.Status],sssSegment,.40:
JSt [EAX+SSS.Purpose],sssPurposeSTACK,.60:
.40:ListGetPrev EAX
JNZ .10:
.50:Invoke SssCreateSe::,[EBX+PGM.CurrentStm],0, \ No stack segment found. Implicit [STACK] will be created.
=B"STACK",5,sssWidth16+sssSegment+sssStack+sssImplicit+sssUsed,sssPurposeSTACK, 16
MOV ECX,[EBX+PGM.Pgmopt.SizeOfStackCommitLow]
MOV [EAX+SSS.TopLow],ECX
.60:CMPD [EAX+SSS.GroupPtr],0
JNZ .90: ; Stack segment should be grouped in MZ format.
MOV EDI,EAX ; Assign stack segment EDI to an existing group.
ListGetFirst [EBX+PGM.SssList]
.70:JNSt [EAX+SSS.Status],sssGroup,.80:
JNSt [EAX+SSS.Purpose],sssPurposeSTACK,.80:
MOV [EDI+SSS.GroupPtr],EAX
.80:ListGetNext EAX
JNZ .70:
.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