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