| Index | Manual | Download | Source | Macros | Embedded word Case ins.
Sitemap | Links | Forum | Tests | Projects

# pfmz.htm
Structures
PFMZ_DOS_HEADER
Procedures
PfmzCompile
PfmzDefaultStack
PfmzLoadPgm

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
  Header of DOS executable format MZ.

    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 OutputStream, Pgm
  PfmzCompile is constructor of output file image in format MZ (16bit DOS executable).
Input
  OutputStream is pointer to a STREAM for the output image contents.
  Pgm is pointer to PGM representing the completely assembled and combined program.
Output
  OutputStream is filled with output file contents.
Error
  Errors are reported with macro Msg.
Invoked from
  PfOutput
Invokes
  EaBufferAlign EaBufferRelease EaBufferReserve PfmzDefaultStack PgmEvalEntry PgmGroupByModel PgmLink PgmOrderSegments PgmRelocResolve PgmStreamImage RelocReportUnresolved SymReportUnresolved
Tested by
  t7921 t7922 t7923 t7924 t7925 t7926

    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

↑ PfmzDefaultStack Pgm
  PfmzDefaultStack will create missing implicit segment `[STACK]` with size specified by the option `PROGRAM SIZEOFSTACKCOMMIT=` (factory-default is 8 KB), when these conditions are met:
  - PROGRAM FORMAT=MZ
  - EUROASM AUTOSEGMENT=ON at the end of program
  - No segment named [STACK] exist int the program
  - No segment with PURPOSE=STACK is found on Pgm.SssList.
Input
  Pgm is pointer to PGM representing the completely assembled and combined program.
Output
  -
Error
  -
Invoked from
  PfOutput
Invokes
  SssCreateSe SssFindByName
Invoked by
  PfmzCompile

    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 BasePgm, ObjBegin, ObjEnd, FileNamePtr
  Executable format MZ is not linkable, it cannot be loaded.
Input
  BasePgm is pointer to an existing PGM to which the object file is being linked/imported.
  ObjBegin is pointer to the contents of linked/imported object file mapped in memory by the caller.
  ObjSize is number of bytes in the object file.
  FileNamePtr is pointer to zero-terminated object file name (used in error reports).
Output
  -
Error
  E8534 Format !1S of file "!2$" is not linkable.
Invoked from
  PfLoad

    PfmzLoadPgm Procedure BasePgm, ObjBegin, ObjSize, FileNamePtr
        Msg '8534',Dict_FormatMZ::,[%FileNamePtr] ; Format !1S of file "!2$" is not linkable.
       EndProcedure PfmzLoadPgm

      ENDPROGRAM pfmz

▲Back to the top▲