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. INCLUDEHEAD \ ; Include headers of another modules used in this module. dict.htm, \ ea.htm, \ eaopt.htm, \ exp.htm, \ msg.htm, \ pf.htm, \ \pfpe.htm, \ pgm.htm, \ pgmopt.htm, \ reloc.htm, \ sss.htm, \ stm.htm, \ sym.htm, \ syswin.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 ;;;ParaRelocNr LocalVar ; Number of PARA# relocations. 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 inPgm.SegOrdBuffer
. ; Copy pointers to unresolved paragraph relocations from all segments to %ParaRelocBuffer. ;;;; XOR EDX,EDX ; Count the number of PARA relocations in EDX. 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 ;; JSt [ESI+RELOC.Status],relocResolved,.77: 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