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

# chunk.htm
Class
CHUNK
CHUNK_HEAD
Encodings
ChunkEnc
Procedures
ChunkFilesCompare
ChunkInclude
ChunkSplit
ChunkSuboperate
ChunkSubHead
ChunkTotalLines

Chunk is a structure that represents a continuous area of memory-mapped source text. Chunks are kept on Src.ChunkList. When €ASM reads the source, it actually reads the list of chunks, one after another.

At the start of source file assembly there is just one chunk reaching the whole source file contents. When `INCLUDE` statement is encounterred, the chunk will split in two and a new chunk with included source contents will be inserted between them.

In closer approach it is little bit more complicated. The initial chunk list consists of three chunks. The first and the last are envelope chunks and they contain only one statement `PROGRAM` and `ENDPROGRAM`, which is the default program used if no PROGRAM block is explicitly defined in the source.

Special chunks will accompany each inserted INCLUDE chunk. Their purpose is proper presentation of included source lines in listing.

          EUROASM
    chunk PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
          INCLUDEHEAD euroasm.htm, \  Interface (structures, symbols and macros) of other modules used in this source.
                      ctx.htm, ea.htm, eaopt.htm, exp.htm, lst.htm, member.htm, msg.htm, pgm.htm, pgmopt.htm, src.htm, stm.htm

    chunk HEAD ; Modul interface.

↑ CHUNK
  Structure which describes chunks of source file.

    CHUNK     STRUC
    .Bottom  D DWORD ; Pointer to the first physical line in the chunk memory.
    .Top     D DWORD ; Pointer to the end of the chunk memory (right behind the last LF).
    .FilePtr D DWORD ; Pointer to FILE of corresponding source file (Ea.SrcFile on Src.FileList).
    .Status  D DWORD ; Binary flags in ChunkEnc encoding.
      ENDSTRUC CHUNK

↑ CHUNK_HEAD
  Structure CHUNK_HEAD represents HEAD/ENDHEAD block used to maintain nesting of HEAD block in sources. CHUNK_HEAD records are kept on their own stack specified by `SRC.HeadStack`.

    CHUNK_HEAD STRUC
    .IdPtr   D DWORD ; Pointer to HEAD statement identifier (the label field of HEAD pseudoinstruction).
    .IdSize  D DWORD ; Size of HEAD block identifier, may be 0.
    .LinePtr D DWORD ; Pointer to the physical line with HEAD statement.
      ENDSTRUC CHUNK_HEAD

↑ ChunkEnc
  Encoding of CHUNK status flags. See also SrcAssemble.
  `chunkError` together with `chunkMsgIdMask` is used to propagate errors discovered in program pass 1 to the final pass and listing.

    chunkEnvelope   = 0x0001 ; Contains implicit PROGRAM/ENDPROGRAM statement inserted by €ASM.
    chunkSource     = 0x0002 ; Chunk with statements from the main source file.
    chunkIncluded   = 0x0004 ; Chunk with statements from included file.
    chunkOrig       = 0x0010 ; Chunk with original INCLUDE* statement. It may have wildcards and multiple suboperated operands.
    chunkResolved   = 0x0020 ; Chunk with INCLUDE statement with one resolved (wildcard-expanded) file only, inserted by €ASM.
    chunkOrigBin    = 0x0040 ; Chunk with original INCLUDEBIN statement with multiple suboperated operands.
    chunkBin        = 0x0080 ; Contains binary data to emit instead of source code.
    chunkError      = 0x0100 ; Contains parameter !1S of message specified in chunkMsgIdMask.
    chunkSkipped    = 0x0200 ; This resolved chunk was skipped due to INCLUDE1 or INCLUDEHEAD1.
    chunkReplace    = 0x2000 ; Chunk with Msg (chunkError) overwrites previous source chunk instead of inserting a new one.
    chunkAbort      = 0x8000 ; Chunk with error which aborts further wildcard resolving.
    chunkSpecial    = chunkError|chunkOrig|chunkResolved|chunkBin|chunkOrigBin
    chunkMsgIdMask  = 0xFFFF_0000 ; Binary encoded MsgId, e.g. 0x1AFF_0000 for MsgId '6911'.

      ENDHEAD chunk ; End of modul interface.
    [.text]

↑ ChunkSplit ChunkPtr, LinePtr

  Procedure ChunkSplit will split the chunk, that contains the line specified by LinePtr, into two chunks. LinePtr then becomes both lower `Chunk.Top` and new higher `Chunk.Bottom`.
  If LinePtr was at the beginning or at the end of chunk, ChunkSplit creates a new empty chunk and puts it on Src.ChunkList. The new chunk inherits `.FilePtr` and flag `chunkIncluded`.

  Src.ChunkList is first searched for LinePtr in order to find which chunk should be split. Search starts in the chunk specified by ChunkPtr, or in the first chunk when ChunkPtr=0.
  Number of chunks is always increased by 1 after ChunkSplit invocation.

Input
  ChunkPtr pointer to the CHUNK where the search for line begins. It may be 0.
  LinePtr pointer to the line in source text where the split should happen.
Output
  CF=0 EAX= Pointer to the second chunk.
Error
  CF=1 EAX=0 Errors are reported with macro Msg.
Example
  Before After ChunkSplit 0,LinePtr ┌─────────┐<───┐ ┌─────────┐<───────┐ │ stm7 │ │ │ stm7 │ │ │ stm6 │ │ │ stm6 │ │ LinePtr->│ stm5 │ │ LinePtr-> ┌─────────┐<───┐ │ stm5 │<─────┐ │ │ stm4 │ │ │ stm4 │ │ └─────────┘ │ │ │ stm3 │ │ │ stm3 │ │ │ │ │ stm2 │ │ │ stm2 │ │ │ │ │ stm1 │<─┐ │ │ stm1 │<─┐ │ │ │ └─────────┘ │ │ └─────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌───old───┐ │ │ ┌──lower──┐ │ │ │ │ │.Bottom │──┘ │ │.Bottom │───┘ │ │ │ │.Top │────┘ │.Top │─────┘ │ │ ChunkPtr->└─────────┘ ChunkPtr─>└─────────┘ │ │ │ │ ┌──higher─┐ │ │ │.Bottom │─────────────────────────────┘ │ │.Top │───────────────────────────────┘ EAX────>└─────────┘
Invoked by
  ChunkInclude
Tested by
  t2401

    ChunkSplit Procedure ChunkPtr, LinePtr
          MOV EAX,[%ChunkPtr]
          MOV EDX,[%LinePtr]
          TEST EAX
          JNZ .10:
          ListGetFirst [Src.ChunkList::]
          JZ .Err:
    .10:  CMP EDX,[EAX+CHUNK.Bottom]
          JB .20:
          CMP EDX,[EAX+CHUNK.Top]
          JBE .30:
    .20:  ListGetNext EAX
          JNZ .10
    .Err: Msg '6909',EDX ; Unspecified include error at "!1_".',0
          SUB EAX,EAX
          STC
          JMPS .90:
    .30:  MOV ESI,EAX
          ; LinePtr EDX is within old chunk ESI.
          ListInsert [Src.ChunkList::],ESI,0
          JC .Err:
          MOV EDI,EAX ; New zeroed chunk which follows the chunk ESI.
          MOV EAX,[ESI+CHUNK.FilePtr]
          MOV EBX,[ESI+CHUNK.Status]
          MOV ECX,[ESI+CHUNK.Top]
          AND EBX,chunkIncluded ; Only this flag is inherited from old chunk.
          MOV [EDI+CHUNK.FilePtr],EAX
          MOV [EDI+CHUNK.Status],EBX
          MOV [EDI+CHUNK.Top],ECX
          MOV [EDI+CHUNK.Bottom],EDX
          MOV [ESI+CHUNK.Top],EDX
          CLC
    .90:  MOV [%ReturnEAX],EDI
    .99:EndProcedure ChunkSplit

↑ ChunkTotalLines
  ChunkTotalLines counts physical lines of all currently allocated source chunks.
Input
  -
Output
  EAX= number of physical lines in current source and all included chunks.
Error
  -
Invoked by
  PgmDestroy

    ChunkTotalLines Procedure
         MOVD [%ReturnEAX],0 ; Initialize counter output.
         ListGetFirst [Src.ChunkList::]
         JZ .90:
     .10:MOV EBX,EAX
         JSt [EBX+CHUNK.Status],chunkSpecial|chunkEnvelope,.80: ; Special chunks do not count.
         MOV EDI,[EBX+CHUNK.Bottom]
         MOV ECX,[EBX+CHUNK.Top]
         SUB EDX,EDX ; Line counter.
         SUB ECX,EDI
         MOV AL,10 ; Line feed.
         DEC EDX
     .30:INC EDX
         REPNE SCASB
         JE .30:
         ADD [%ReturnEAX],EDX
     .80:ListGetNext EBX
         JNZ .10:
     .90:EndProcedure ChunkTotalLines

↑ ChunkSuboperate ChunkPtr, TxtPtr, TxtEnd, ReportsMsg
  ChunkSuboperate will parse and execute one suboperation operator specified in text TxtPtr..TxtEnd.
  Suboperation, if present, modifies the Chunk.Bottom and Chunk.Top.
Input
  ChunkPtr is pointer to the chunk which describes source-file contents before the actual suboperation is applied.
  TxtPtr is pointer to the suboperator. The first character should be `[` or `{`, otherwise the procedure does nothing. Parsing stops at the corresponding `]` or `}`.
  TxtEnd points to the end of text where parsing must stop.
  ReportsMsg is a Boolean value in MsgEncoding which controls if detected errors will be reported with macro Msg.
Output
  CF=0, ZF=0, EAX=TxtPtr: Chunk is unchanged when TxtPtr was not at `[` or `{` (no suboperation found).
  CF=0, ZF=1 on succesfull suboperation. EAX= pointer behind the parsed text (the character following suboperator end `]` or `}`. EAX is always between TxtPtr and TxtEnd. The contents of chunk (CHUNK.Bottom, CHUNK.Top) was modified according to suboperation.
Error
  CF=1 Errors are reported with macro Msg if ReportMsg is nonzero.
Example
  .NextSubop: Invoke ChunkSuboperate,EBX,ESI,EDX,msgSuppress JC .Error: MOV ESI,EAX JZ .NextSubop: ; Suboperations may be chained.
See also
  VarSuboperate
Invokes
  EaBufferRelease EaBufferReserve ExpConvertToNumber ExpEval ExpParseRange ExpParseSuboperation VarExpand
Invoked by
  ChunkInclude SrcCreate
Tested by
  t1220

    ChunkSuboperate Procedure ChunkPtr, TxtPtr, TxtEnd, ReportsMsg
    ChSopBuffer   LocalVar ; Buffer for computing suboperation range.
    ChSopLength   LocalVar ; Value of %&.
    ChSopLenDec   LocalVar Size=12 ; Value of %& as decimal number.
    ChSopLenSize  LocalVar ; Number of digits in ChSopLenDec
    ChSopChar     LocalVar ; One character copied to ChSopBuffer.
    ChSopExp      LocalVar Size=SIZE#EXP ; Room for expression evaluating.
    ChSopLeft     LocalVar ; Left range value.
    ChSopRight    LocalVar ; Right range value.
    ChSopRightPtr LocalVar ; Pointer to the right range text.
    ChSopRightEnd LocalVar ; End of the right range text.
        MOV EBX,[%ChunkPtr]
        MOV EDI,[%TxtPtr]
        SUB ECX,ECX
        MOV [%ReturnEAX],EDI ; First assume no valid suboperation.
        DEC ECX
        CMP EDI,[%TxtEnd]
        JNB .99: ; If the text is empty.
        MOV CL,[EDI]
    .10:CMP CL,'['
        JE .20:
    .15:CMP CL,'{'
        CLC
        JNE .99: ; The text is not a suboperation.
    .20:MOVD [%ChSopLeft],1 ; Initialize with default=1
        MOV EDX,[EBX+CHUNK.Top]
        MOV EDI,[EBX+CHUNK.Bottom]
        SUB EDX,EDI
        CMP CL,'['
        JE .40: ; If suboperator is substring [..], %&=EDX is filesize.
      ; Suboperator is sublist {..}, %& will be set to the number of physical lines.
        MOV ECX,EDX ; File size.
        MOV AL,10 ; End of line.
        SUB EDX,EDX ; Line counter.
    .30:JECXZ .40:
        INC EDX
        REPNE SCASB
        JE .30:
    .40:MOV [%ChSopLength],EDX
        MOV [%ChSopRight],EDX ; Initialize with default=%& value.
        MOV EAX,EDX ; %&.
        LEA ESI,[%ChSopLenDec]
        StoD ESI,Size=12 ; Convert the binary %& to decimal number.
        SUB EDI,ESI
        MOV [%ChSopLenSize],EDI ; Number of decimal digits.
        Invoke EaBufferReserve::, ChunkSuboperate
        MOV [%ChSopBuffer],EAX
        Invoke ExpParseSuboperation::,[%TxtPtr],[%TxtEnd] ; Find the corresponding ] or }.
        MOV [%ReturnEAX],ESI ; Position behind the closing ] or }.
        JC .E6911: ; Wrong file suboperation "!1S".
        ; Copy suboperator to [%ChSopBuffer], expanding %& to %ChSopLenDec.
        MOV EDX,ESI ; End of suboperator.
        MOV ESI,[%TxtPtr]
        LEA EDI,[%ChSopChar]
    .50:CMP ESI,EDX
        JNB .80:
        LODSB
        CMP AL,'%%'
        JE .60:
        MOV [EDI],AL
        BufferStore [%ChSopBuffer],EDI,1
        JMP .50:
    .60:LODSB
        CMP AL,0x26 ; '&'.
        JNE .70:
        LEA EAX,[%ChSopLenDec]
        BufferStore [%ChSopBuffer],EAX,[%ChSopLenSize]
        JMP .50:
    .70:SUB ESI,2 ; ESI points to %variable other than %&, it will be expanded as usual.
        Invoke EaBufferReserve::,ChunkSuboperate.70
        MOV EBX,EAX
        Invoke VarExpand::,ESI,EDX,EBX,-1 ; Returns EAX behind the parsed %variable[subop].
        JC .75:
        BufferRetrieve EBX
        BufferStore [%ChSopBuffer],ESI,ECX
        Invoke EaBufferRelease::,EBX
        MOV ESI,EAX ; Behind the parsed %variable[].
        JMP .50:
    .75:Invoke EaBufferRelease::,EBX
        JMP .E6911:
    .80:BufferRetrieve [%ChSopBuffer]
        INC ESI
        SUB ECX,2 ; Strip off [] {}
        JB .E6911: ; Wrong file suboperation "!1S".
        ; ESI,ECX is now a range (expanded suboperation without braces).
        LEA EDX,[ESI+ECX]
        LEA EBX,[%ChSopExp]
        Invoke ExpParseRange::,ESI,EDX
        JNC .Range:
      ; No range, only one operand ESI..EDX is in braces.
        SUB EDX,ESI
        Invoke ExpEval::,EBX,ESI,EDX,0
        JC .Error:
        JZ .RangeEvaluated: ; Empty left value - use default 1.
        Invoke ExpConvertToNumber::,EBX
        MOV ECX,[EBX+EXP.Status]
        MOV EAX,[EBX+EXP.Low]
        JC .E7330:
        CMP CH,expWidth8B
        JNB .E7330: ; Plain 32bit num. range value expected instead of expr.type "!1Z".
        MOV [%ChSopLeft],EAX
        MOV [%ChSopRight],EAX
        JMP .RangeEvaluated:
    .Range: ; Range specified.     Left range operand is ESI..EAX-2
         MOV [%ChSopRightPtr],EAX ;  Right range operand is EAX..EDX, store for later.
         MOV [%ChSopRightEnd],EDX
         SUB EAX,2
         SUB EAX,ESI
         Invoke ExpEval::,EBX,ESI,EAX,0
         JC .Error:
         JZ .RR: ; Empty left range, leave it to default=1.
         Invoke ExpConvertToNumber::,EBX
         MOV ECX,[EBX+EXP.Status]
         MOV EAX,[EBX+EXP.Low]
         JC .E7330:
         CMP CH,expWidth8B
         JNB .E7330:
         MOV [%ChSopLeft],EAX
         MOV EAX,[%ChSopRightEnd]
         MOV ECX,[%ChSopRightPtr]
         SUB EAX,ECX
    .RR: Invoke ExpEval::,EBX,ECX,EAX,0
         JC .Error:
         JZ .RangeEvaluated:
         Invoke ExpConvertToNumber::,EBX
         MOV ECX,[EBX+EXP.Status]
         MOV EAX,[EBX+EXP.Low]
         JC .E7330:
         CMP CH,expWidth8B
         JNB .E7330:
         MOV [%ChSopRight],EAX
    .RangeEvaluated:  ; The actual suboperation with range %ChSopLeft..%ChSopRight takes place here.
         MOV EBX,[%ChunkPtr]
         MOV ECX,[%ChSopLeft]
         MOV ESI,[EBX+CHUNK.Bottom]
         MOV EDX,[EBX+CHUNK.Top]
         MOV EDI,[%TxtPtr]
         CMPB [EDI],'['
         JNE .Sublist:
     ; Substring operation.
         LEA EAX,[ESI+ECX-1]
         CMP EAX,EDX
         JNA .S1:
         MOV EAX,EDX
    .S1: CMP EAX,ESI
         JNB .S2:
         MOV EAX,ESI
    .S2: MOV [EBX+CHUNK.Bottom],EAX
         ADD ESI,[%ChSopRight]
         CMP ESI,EDX
         JNA .S3:
         MOV ESI,EDX
    .S3: CMP ESI,EAX
         JNB .S4:
         MOV ESI,EAX
    .S4: MOV [EBX+CHUNK.Top],ESI
         JMP .End:
    .Sublist: ; operation.
         SUB ECX,1
         MOV EDI,ESI ; Chunk.Bottom
         JLE .L2:
    .L1: CMP EDI,EDX ; Chunk.Top
         JNB .L2:
         PUSH ECX
          MOV ECX,EDX
          MOV AL,10
          SUB ECX,EDI
          REPNE SCASB
         POP ECX
         LOOP .L1:
    .L2: SUB ECX,ECX
         MOV [EBX+CHUNK.Bottom],EDI
         ADD ECX,[%ChSopRight]
         JLE .L4
         MOV EDI,ESI ; Original Chunk.Bottom
    .L3: CMP EDI,EDX ; Chunk.Top
         JNB .L4:
         PUSH ECX
          MOV ECX,EDX
          MOV AL,10
          SUB ECX,EDI
          REPNE SCASB
         POP ECX
         LOOP .L3:
    .L4: MOV [EBX+CHUNK.Top],EDI
         JMP .End:
    .Error:Invoke EaBufferRelease::, [%ChSopBuffer]
         STC
         JMP .99:
    .E6911: ; Wrong file suboperation "!1S".
        CMPD [%ReportsMsg],0
        JZ .Error:
        MOV ESI,[%TxtPtr]
        MOV ECX,[%TxtEnd]
        LEA EDX,[%ChSopExp+4]
        SUB ECX,ESI
        MOV [EDX+0],ESI
        MOV [EDX+4],ECX
        Msg '6911',EDX ; Wrong file suboperation "!1S".
        JMP .Error:
    .E7330:
        CMPD [%ReportsMsg],0
        Msg cc=NZ,'7330',ECX ; Plain 32bit numeric range value expected instead of expr.type "!1Z".
        JMP .Error:
    .End:Invoke EaBufferRelease::,[%ChSopBuffer]
        SUB EAX,EAX ; Set ZF=1.
    .99:EndProcedure ChunkSuboperate

↑ ChunkSubHead ChunkPtr, ChunkBuf
  ChunkSubHead will limit the chunk contents to HEAD/ENDHEAD block(s) only. Statements from the chunk are parsed but not executed, and only those between HEAD and ENDHEAD will be kept in the chunk (including those block statements themselves).
  HEAD/ENDHEAD blocks may occur multiple times and they may be nested, which is maintained by Src.HeadStack.
  ChunkSubHead returns array of chunks in ChunkBuf, one for each distinct HEAD..ENDHEAD block. If there is no HEAD statement in the input chunk, ChunkBuf contains one empty chunk.
Input
  ChunkPtr is pointer to input CHUNK with included contents.
  ChunkBuf is pointer to an empty output BUFFER where the subheaded chunk(s) will be stored (at least one).
Output
  CF=0 Status of the chunk is set to `chunkInclude` and the chunk's .Bottom and .Top is restrained to HEAD/ENDHEAD block.
Error
  chunkError is returned.
Invokes
  CtxPeek StmClean StmCreate StmDestroy StmParse
Invoked by
  ChunkInclude
Tested by
  t1230 t1233

    ChunkSubHead Procedure ChunkPtr, ChunkBuf
    CshChunkLeaf  LocalVar Size=4+4+SIZE#CHUNK ; Fake ChunkList with only one member - the entire INCLUDEHEADed file contents.
    CshNewChunk   LocalVar Size=SIZE#CHUNK ; Working place for the new chunkInclude with head block contents only.
    CshChunkHead  LocalVar Size=SIZE#CHUNK_HEAD ; Working place for the nested CHUNK_HEAD record.
    CshStm        LocalVar Size=SIZE#STM ; Fake statement for parsing the included chunk.
    CshAtLeast1   LocalVar ; Nonzero if at least one HEAD block was found in chunk contents.
    CshPgmPtr     LocalVar ; ^PGM of current program. Used to block error messages during HEAD/ENDHEAD parsing.
         StackClear [Src.HeadStack] ; Start with empty Src.HeadStack (outside HEAD block).
         ; Prepare environment for parsing HEAD..ENDHEAD blocks in chunk contents.
         MOV EDX,[%ChunkPtr]
         XOR EAX,EAX
         LEA EDI,[%CshChunkLeaf]
         MOV [%CshAtLeast1],EAX
         STOSD ; LIST.Next. Simulate solo leaf with just one chunk for SrcFetchLine.
         STOSD ; LIST.Prev.
         CopyTo EDI,EDX,Size=SIZE#CHUNK
         MOV EDX,EDI
         ; EDX is now pointer to the fake input chunk with entire included file contents. No more source follows the chunk.
         Invoke CtxPeek::,ctxPROGRAM,0
         JC .10:
         MOV ECX,[EAX+CTX.ObjPtr]
         MOV [%CshPgmPtr],ECX
         JECXZ .10:
         SetSt [ECX+PGM.Status],pgmNoMsg ; Supress error messages detected during the parsing of fake chunk.
    .10: LEA EBX,[%CshStm]
         Invoke StmCreate::,EBX
         MOV EAX,[EDX+CHUNK.Bottom]
         MOV [EBX+STM.Program],ECX
         MOV [EBX+STM.ChunkPtr],EDI
         MOV [EBX+STM.LinePtr],EAX
         MOV [EBX+STM.LineEnd],EAX
         LEA EDI,[%CshNewChunk]
         CopyTo EDI,EDX,Size=SIZE#CHUNK ; EDI is now the target chunk dedicated for HEAD..ENDHEAD contents only.
    .20: MOV [EDI+CHUNK.Bottom],EAX ;
         MOV [EDI+CHUNK.Top],EAX ; The output chunk starts as empty.
    .30: ; Parse the chunk EDX contents in the loop .30: .. .60:.
         MOV EAX,[EBX+STM.LineEnd]
         MOV EDX,[EBX+STM.ChunkPtr]
         Invoke StmClean::,EBX
         Invoke StmParse::,EBX,EAX,EDX
         JC .60: ; Parsing is over at the end of chunk contents.
         ; EBX is the parsed statement from the source chunk.
         MOV EAX,[EBX+STM.OperationData]
         Dispatch EAX,PseudoHEAD::,PseudoENDHEAD::
         JMP .30: ; Ignore any other statement than HEAD and ENDHEAD and parse the next one.
    .PseudoHEAD: ; EBX is HEAD statement.
         StackPeekLast [Src.HeadStack::] ; Inside the HEAD/ENDHEAD block?
         JNC .40: ; Skip if the HEAD context is already inside another HEAD block.
         ; HEAD block begins.
         MOV EAX,[EBX+STM.LinePtr]
         LEA EDI,[%CshNewChunk]
         MOV [EDI+CHUNK.Bottom],EAX
         MOV [EDI+CHUNK.Top],EAX
     .40:; Create and push CHUNK_HEAD record on Src.HeadStack.
         LEA EDI,[%CshChunkHead]
         MOV ESI,[EBX+STM.LabelPtr]
         MOV ECX,[EBX+STM.LabelSize]
         MOV [EDI+CHUNK_HEAD.LinePtr],EAX
         PoolStore [Src.Pool::],ESI,ECX  ; Make the HEAD block identifier permanent.
         MOV [EDI+CHUNK_HEAD.IdPtr],EAX
         MOV [EDI+CHUNK_HEAD.IdSize],ECX
         StackPush [Src.HeadStack::],EDI
         LEA EDI,[%CshNewChunk] ; Restore the output chunk again.
         JMP .30: ; Continue parsing the chunk contents.
    .PseudoENDHEAD: ; EBX is ENDHEAD statement.
         StackPop [Src::+SRC.HeadStack]
         JC .E6917: ; Wrong nesting, ENDHEAD without HEAD in file "!1S".
         BufferRetrieve [EBX+STM.OrdBuffer] ; ENDHEAD identifier, if any.
         JECXZ .50: ; EndBlock identifier may be empty, that's always OK.
         MOV ECX,[ESI+4]
         MOV ESI,[ESI+0] ; ENDHEAD identifier must match the one from CHUNK_HEAD.
         StripSpaces ESI,ECX
         StripColons ESI,ECX
         JECXZ .50: ; EndBlock identifier may be empty, that's always OK.
         CMP ECX,[EAX+CHUNK_HEAD.IdSize]
         JNE .E6919: ; Mismatched HEAD/ENDHEAD identifier in file "!1S"
         MOV EDI,[EAX+CHUNK_HEAD.IdPtr]
         REPE CMPSB
         JNE .E6919: ; Mismatched HEAD/ENDHEAD identifier in file "!1S"
     .50:StackPeekLast [Src.HeadStack::] ; Still inside a HEAD block?
         JNC .30: ; If inside a nested HEAD/ENDHEAD block, continue with the same CshNewChunk.
         ; HEAD..ENDHEAD block has been closed. Put it to CshNewChunk, store to ChunkBuf and continue parsing.
         LEA EDI,[%CshNewChunk]
         MOV EAX,[EBX+STM.LineEnd] ; End of line with ENDHEAD pseudoinstruction.
         MOV [EDI+CHUNK.Top],EAX
         MOV [%CshAtLeast1],EAX
         BufferStore [%ChunkBuf],EDI,SIZE#CHUNK
         JMP .20: ; Empty CshNewChunk and continue parsing the chunk contents.
    .60: ; All lines from source chunk are parsed or error occured.
         StackPop [Src.HeadStack::] ; HeadStack should be empty.
         JNC .E6918:
         XOR EAX,EAX
         CMP [%CshAtLeast1],EAX
         JNZ .80: ; OK, block was found.
    .W3470:MOV EDX,3470<<16+chunkError ; No valid HEAD/ENDHEAD block found in the file "!1S". >>
         JMPS .Err:
    .E6917:MOV EDX,6917<<16+chunkError ; Wrong nesting, ENDHEAD without HEAD in file "!1S". >>
         JMPS .Err:
    .E6918:MOV EDX,6918<<16+chunkError ; Wrong nesting, missing ENDHEAD in file "!1S". >>
         JMPS .Err:
    .E6919:MOV EDX,6919<<16+chunkError ; Mismatched HEAD/ENDHEAD identifier in file "!1S". >>
    .Err:LEA EDI,[%CshNewChunk]
         MOV [EDI+CHUNK.Status],EDX
         ; On error the returned chunk EDI will contain Msg parameter !1 (filename) instead of source text.
         MOV EBX,[EDI+CHUNK.FilePtr]
         LEA ESI,[EBX+FILE.Name]
         GetLength$ ESI
         JECXZ .75:
         PUSH ECX,ESI
    .65:   LODSB
           CMP AL,'\'
           JNE .70:
           MOVB [ESI-1],'/'
    .70:   LOOP .65:
         POP ESI,ECX
    .75: PoolStore [Src.Pool::],ESI,ECX
         MOV [EDI+CHUNK.Bottom],EAX
         ADD EAX,ECX
         MOV [EDI+CHUNK.Top],EAX
         BufferClear [%ChunkBuf]
         BufferStore [%ChunkBuf],EDI,SIZE#CHUNK ; Replace chunk(s) in output array with the chunkError.
    .80: LEA EBX,[%CshStm]
         Invoke StmDestroy::,EBX
         MOV ECX,[%CshPgmPtr]
         JECXZ .90:
         RstSt [ECX+PGM.Status],pgmNoMsg ; Cancel supression of parsing errors.
     .90:EndProcedure ChunkSubHead

↑ ChunkFilesCompare File1, File2
  ChunkFilesCompare compares contents of two files. This is used in pseudoinstructions INCLUDE1 and INCLUDEHEAD1.
Input
  File1, File2 are pointers to FILE object. Both files must be assigned and memory-mapped.
Output
  CF=0, ZF=1 if the contents of both files is identical.
  CF=0, ZF=0 if the contents of both files differs.
  CF=1, ZF=0 if some file was not succesfully mapped to memory.
Invoked by
  ChunkInclude

    ChunkFilesCompare Procedure File1, File2
       MOV EDI,fileStOpened+fileStMapped+fileStMapOpened
       MOV EBX,[%File1]
       MOV EAX,[EBX+FILE.Status]
       AND EAX,EDI
       CMP EAX,EDI
       STC
       JNE .90:
       MOV EDX,[%File2]
       MOV EAX,[EDX+FILE.Status]
       AND EAX,EDI
       CMP EAX,EDI
       STC
       JNE .90:
       MOV ECX,[EBX+FILE.BufSize]
       CMP ECX,[EDX+FILE.BufSize]
       CLC
       JNE .90:
       MOV ESI,[EBX+FILE.BufPtr]
       MOV EDI,[EDX+FILE.BufPtr]
       REPE CMPSB
       CLC
    .90:EndProcedure ChunkFilesCompare

↑ ChunkInclude Stm
  ChunkInclude is the common handler for INCLUDE* pseudoinstructions. It is invoked usually in pass 1 only and it modifies the source chunk list. The source line with INCLUDE* statement will be replaced with included contents and some special chunks inserted which keep the listing in pace.
  Inserted special chunkOrig and chunkResolved are displayed in listing but not assembled in the following program passes.
  ChunkInclude is invoked from PseudoINCLUDEBIN PseudoINCLUDE PseudoINCLUDE1 PseudoINCLUDEHEAD PseudoINCLUDEHEAD1 with Stm.Status flags `stmIncludeBin`, `stmIncludeHead` and/or `stm1` specifying which operation was the handler invoked from.
Example
  Before ChunkInclude the whole primary source is in one ordinary chunk: ┌──────────────────┐ │ Src PROGRAM │ chunkEnvelope (autoinserted statement `Src PROGRAM`). ├──────────────────┤ │ Src statements1 │ │ │ chunkSource (entire primary source) │ Include Inc*.asm │ │ Src statements3 │ ├──────────────────┤ │ ENDPROGRAM Src │ chunkEnvelope (autoinserted statement `ENDPROGRAM Src`). └──────────────────┘ After ChunkInclude: ┌───────────────────┐ │ Src PROGRAM │ chunkEnvelope (autoinserted statement `Src PROGRAM`). ├───────────────────┤ │ Src statements1 │ chunkSource (statements preceeding `Include Inc*.asm`) ├───────────────────┤ │ Include Inc*.asm │ chunkOrig (original `Include Inc*.asm` statement) ├───────────────────┤ │ INCLUDE IncA.asm │ chunkResolved (autoinserted statement with one filename) ├───────────────────┤ │ IncA statements │ chunkIncluded (statements from included file) ├───────────────────┤ │ INCLUDE IncB.asm │ chunkResolved (autoinserted statement with one filename) ├───────────────────┤ │ IncB statements │ chunkIncluded (statements from included file) ├───────────────────┤ │ Src statements3 │ chunkSource (statements following `Include Inc*.asm`) ├───────────────────┤ │ ENDPROGRAM Src │ chunkEnvelope (autoinserted statement `ENDPROGRAM Src`). └───────────────────┘
See also
  CHUNK, manual INCLUDE.
Invokes
  ChunkFilesCompare ChunkSplit ChunkSubHead ChunkSuboperate CtxFind EaBufferRelease EaBufferReserve EaoptGetOnePath StmCheckFields
Invoked by
  PseudoINCLUDE PseudoINCLUDE1 PseudoINCLUDEBIN PseudoINCLUDEHEAD PseudoINCLUDEHEAD1
Tested by
  t1233 t2402 t2403 t2404

    ChunkInclude Procedure Stm
    CiOrigChunk   LocalVar ; Pointer to the chunk with original INCLUDE* statement.
    CiPrevChunk   LocalVar ; Pointer to the last chunk (previously inserted). At first it is chunkOrig, more chunks will follow.
    CiPathNr      LocalVar ; Number of path from INCLUDEPATH= option (0,1,2..)
    CiSubopBegin  LocalVar ; Suboperation definition, may be chained, e.g. {1..30}.
    CiSubopPtr    LocalVar ; Pointer to suboperation SubopBegin..SubopEnd.
    CiSubopEnd    LocalVar
    CiFilemaskPtr LocalVar ; Unquoted file name and extension, may contain wildcards and path.
    CiFilemaskEnd LocalVar ; [%FilemaskPtr]..[%FilemaskEnd] is contents of one operand in INCLUDE* statement.
    CiDosDateTime LocalVar ; Last write time of included file.
    CiIncFile     LocalVar Size=SIZE#FILE ; Included file. It may be copied to Src.FileList later.
        ClearLocalVar
        MOV EBX,[%Stm]
        JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99:
        SetSt [Src.Lst.Status::],lstNoData+lstSectKeep
        StmInStruc EBX,.99: ; Do not allow this statement in a structure definition.
        Invoke StmCheckFields::,EBX,"00*0"
        JC .99:
         ; Split the current chunk twice into 3 chunks: lower, chunkOrig and higher,
         ;      where chunkOrig will contain only the EBX statement INCLUDE* files*,morefiles*
        Invoke ChunkSplit,[EBX+STM.ChunkPtr],[EBX+STM.LinePtr]
        JC .99:
        MOV [%CiPrevChunk],EAX ; EAX is now chunk which begins with INCLUDE* statement.
        MOV [%CiOrigChunk],EAX ; EAX is now chunk which begins with INCLUDE* statement.
        MOV ECX,chunkOrigBin
        JSt [EBX+STM.Status],stmIncludeBin,.40:
        MOV ECX,chunkOrig
    .40:SetSt [EAX+CHUNK.Status],ECX
       ; Isolate the solo statement EBX into chungOrig EAX.
        Invoke ChunkSplit,EAX,[EBX+STM.LineEnd]
        JC .99:
       ; Source chunk is now split into three,
       ;    pointer to the middle one (chunkOrig/chunkOrigBin) with solo INCLUDE* statement
       ;    is in [%CiChunkOrig] and copied to [%CiPrevLeaf].
        Invoke CtxFind::,ctxRepeat,EBX ; Warn if INCLUDE* in repeating block.
        JC .45: ; If not in repeating context.
        MOV EDX,3610<<16+chunkError+chunkAbort ; File inclusion in repeating block !1S is not supported.    >>
        MOV ESI,[EAX+CTX.NamePtr]
        MOV ECX,[EAX+CTX.NameSize]
        CALL .Error:
    .45:; Now it's time to process statement's operands in the loop and
       ; to insert each resolved filename to chunkResolved (inserted upon [%CiPrevChunk])
       ; and then insert the filename contents to chunkIncluded (inserted upon chunkResolved).
        BufferRetrieve [EBX+STM.OrdBuffer]           ; At least one operand is present.
        LEA EDX,[ESI+ECX]                            ; End of OrdBuffer.
    .50:LODSD                                        ; Pointer to operand.
        MOV EDI,EAX
        LODSD                                        ; Size of operand.
        PUSH EDX,ESI
         CALL .Operand:
        POP ESI,EDX
        CMP ESI,EDX
        JB .50:                                      ; If more than one operand in the statement.
        JMP .99:

    .Operand: PROC ; Include file(s) from one operand.
                   ; Input: EDI,EAX is the operand to include. EBX is statement. EBP is stack frame.
                   ; Output: EBX,EBP must be preserved.
         MOV EDX,EAX ; Size of the included filename. It may contain path, wildcards, suboperations.
         StripSpaces EDI,EDX
         TEST EDX
         JZ .O9:
         CMP EDX,1
         JA .O0:
         MOV AL,[EDI]
         ExpClassify AL
         TEST AH,expLetter|expDigit
         JZ .O9:
    .O0: MOV [%CiFilemaskPtr],EDI; First suppose no quotes and no suboperations, e.g. file*.asm.
         LEA EAX,[EDI+EDX]
         MOV [%CiFilemaskEnd],EAX
         MOV [%CiSubopBegin],EAX                     ; Also suppose no suboperation.
         MOV [%CiSubopPtr],EAX
         MOV [%CiSubopEnd],EAX
         MOV ESI,EDI
         MOV ECX,EDX
         LODSB
         CMP AL,'"'
         JNE .O1:
         MOV EDI,ESI                                 ; Filename is in quotes.
         DEC ECX
         MOV [%CiFilemaskPtr],EDI
         REPNE SCASB                                 ; Find the terminating double quote.
         JNE .E6910:                                 ; Wrong filename !1S.
         MOV [%CiSubopBegin],EDI                     ; If suboperation is present, it begins here, behind the quote.
         MOV [%CiSubopPtr],EDI
         DEC EDI
         MOV [%CiFilemaskEnd],EDI
    .O1: ; [%CiFilemaskPtr]..[%CiFilemaskEnd] now specifies unquoted filename,
         ;             perhaps with path and wildcards, without suboperations.
         MOV ESI,[%CiFilemaskPtr]
         MOV EDX,[%CiFilemaskEnd]
         SUB EDX,ESI
     QueryChar %MACRO Char                           ; Ad hoc macro to find a Char in string ESI,EDX.
           MOV AL,%Char
           MOV EDI,ESI
           MOV ECX,EDX
           REPNE SCASB
      %ENDMACRO QueryChar
         ; Query if wildcarded (contains asterix or question mark).
         RstSt [EBX+STM.Flags],stmtWildcarded + stmtAtLeast1
         QueryChar '*'
         JE .O2:
         QueryChar '?'
         JNE .O3:
    .O2: SetSt [EBX+STM.Flags],stmtWildcarded
    .O3: ; Query if Filemask was specified with path (contains slash or colon).
         SetSt [EBX+STM.Flags],stmtWithPath
         QueryChar '\'
         JE .O7:
         QueryChar '/'
         JE .O7:
         QueryChar ':'
         JE .O7:
         RstSt [EBX+STM.Flags],stmtWithPath
         ; If no path specified in INCLUDE* ordinal, we must try all pathes from %^INCLUDEPATH.
         XOR EAX,EAX
         MOV [%CiPathNr],EAX               ; Start with the 1st path from %^INCLUDEPATH.
    .O4: Invoke EaoptGetOnePath::,[Ea.Eaopt.IncludePathPtr::],[Ea.Eaopt.IncludePathSize::],[%CiPathNr] ; Get the path to ESI,ECX.
         JC .O8:                           ; If no more path specified in INCLUDEPATH=.
         INCD [%CiPathNr]                  ; Prepare for the next path.
         MOV EDX,[%CiFilemaskEnd]
         SUB EDX,[%CiFilemaskPtr]          ; EDX is now size of filemask without path.
         LEA EAX,[EDX+ECX]                 ; ESI,ECX is one include path.
         CMP EAX,MAX_PATH_SIZE
         JA .E6913:                        ; Path+filemask too long.
         LEA EDI,[%CiIncFile+FILE.Name]    ; Assign path+filemask to CiIncFile.
         REP MOVSB
         MOV AX,'\/'
         CMPB [Ea.EuroasmOS::],'W'         ; Select slash or backslash.
         JE .O5:
         XCHG AL,AH
    .O5: CMP AL,[EDI-1]
         JE .O6:
         CMP AH,[EDI-1]
         JE .O6:
         STOSB                             ; If the path was not terminated with slash or backslash, add it.
    .O6: MOV ESI,[%CiFilemaskPtr]
         MOV ECX,EDX
         REP MOVSB
         SUB EAX,EAX                       ; Zero terminate the filemask.
         STOSB                             ; CiIncFile is now assigned with path and filemask.
         LEA EDI,[%CiIncFile]
         SysEachFile EDI, ChunkInclude.File; Perform callback .File with each wildcard-resolved filename.
         JSt EAX,chunkAbort,.O9:           ; Some errors should stop resolving further files.
         JSt [EBX+STM.Flags],stmtWithPath,.O8:
         JSt [EBX+STM.Flags],stmtWildcarded,.O4: ; Continue search with the next include path.
         JNSt [EBX+STM.Flags],stmtAtLeast1,.O4: ; If non-wildcarded file not found, continue search.
         JMP .O9:
    .O7: LEA EDI,[%CiIncFile+FILE.Name]
         JMP .O6:
    .O8: JSt [EBX+STM.Flags],stmtWildcarded,.O9:
         JNSt [EBX+STM.Flags],stmtAtLeast1,.E6914:
    .O9: RET                               ; Return from ChunkInclude.Operand.

    .E6910:MOV EDX,6910<<16+chunkError     ;> Wrong filename !1S.
         MOV ECX,EDI
         SUB ECX,ESI
         JMP ChunkInclude.Error:
    .E6913:MOV EDX,6913<<16+chunkError     ;> Size of IncludePath "!1S" + size of filename exceeded 256 characters.
         JMP ChunkInclude.Error:
    .E6914:MOV EDX,6914<<16+chunkError     ;> File "!1S" not found.
         MOV ESI,[%CiFilemaskPtr]
         MOV ECX,[%CiFilemaskEnd]
         SUB ECX,ESI
         JMP ChunkInclude.Error:
        ENDP .Operand:

    .File:: PROC  ; Callback from SysEachFile in ChunkInclude.Operand.O6:
      ; Input: EBX=Pointer to unopened FILE with assigned path (perhaps from %^INCLUDEPATH) and non-wildcarded name.
      ;        [%ReturnEBX] pointer to STM with original INCLUDE* pseudoinstruction.
      ;        [%CiOrigChunk] points to chunkOrig with original INCLUDE* pseudoinstruction.
      ;        [%CiPrevChunk] equals [%CiOrigChunk] if no file was included yet, otherwise it points to lastly inserted chunkInclude.
      ; Output: At least two new chunks will be inserted to chunk list right after [%CiPrevChunk]:
      ;           chunkResolved, which contains INCLUDE* File.Name
      ;           chunkIncluded  with the actual included file contents (one or more if multiple HEAD blocks).
      ; On error: chunkError is inserted instead of chunkIncluded, with contents=Msg parameter !1S.
         LEA ESI,[EBX+FILE.Name]                     ; For the case of E6916.
         SysGetFileTime ESI
         MOV [%CiDosDateTime],EAX
         MOV EDX,[%ReturnEBX]                        ; ^STM with pseudoinstruction INCLUDE*.
         MOV ECX,[Src.Inclusions::]                  ; Number of already included files in this source.
         INC ECX
         CMP ECX,[Ea.Eaopt.MaxInclusions::]
         JA .E6916:                                  ; "!1S" - number of included files exceeded MaxInclusions.
         MOV [Src.Inclusions::],ECX                  ; OK, MaxInclusion not exceeded yet, do include file EBX.
         MOV ESI,[%CiOrigChunk]
         MOV ECX,chunkIncluded
         AND ECX,[ESI+CHUNK.Status]                  ; Flag chunkIncluded is the only one inherited from chunkOrig to chunkResolved.
         MOV ESI,[%CiPrevChunk]                      ; Previously included chunk.
         Invoke ChunkSplit,ESI,[ESI+CHUNK.Top]       ; Create chunkResolved.
         MsgUnexpected cc=C
         MOV [%CiPrevChunk],EAX
         SetSt ECX,chunkResolved
         MOV [EAX+CHUNK.Status],ECX                  ; Parent's chunkIncluded (if set) combined with chunkResolved.
         MOV ESI,EAX
          ; Inserted chunkResolved will contain fake statement  INCLUDE* "file"{subop}.
         LEA EDI,[EBX+FILE.Name]
         XOR EAX,EAX
         MOV ECX,SIZE#FILE.Name
         MOV EDX,EDI
         REPNE SCASB
         SUB EDI,EDX                                 ; EDI = resolved filename size + terminating zero.
         LEA ECX,[EDI+20]                            ; Size of file name + the size of token "INCLUDEHEAD1" (the longest).
         ADD ECX,[%CiSubopEnd]
         SUB ECX,[%CiSubopBegin]
         PoolNew [Src.Pool],ECX,Align=BYTE           ; Persistent room for fake title statement.
         MOV EDI,EAX
         MOV [ESI+CHUNK.Bottom],EDI
         MOV EDX,[%ReturnEBX]                        ; Original statement.
         PUSH ESI                                    ; Construction of fake statement in chunkResolved.
           MOV AL,' '                                ; Leading space.
           STOSB
           MOV ESI,[Dict_PseudoINCLUDEBIN::+0]
           MOV ECX,[Dict_PseudoINCLUDEBIN::+4]
           JSt [EDX+STM.Status],stmIncludeBin, .F10:
           MOV ESI,[Dict_PseudoINCLUDE::+0]
           MOV ECX,[Dict_PseudoINCLUDE::+4]
           JNSt [EDX+STM.Status],stmIncludeHead|stm1, .F10:
           MOV ESI,[Dict_PseudoINCLUDE1::+0]
           MOV ECX,[Dict_PseudoINCLUDE1::+4]
           JNSt [EDX+STM.Status],stmIncludeHead, .F10:
           MOV ESI,[Dict_PseudoINCLUDEHEAD::+0]
           MOV ECX,[Dict_PseudoINCLUDEHEAD::+4]
           JNSt [EDX+STM.Status],stm1, .F10:
           MOV ESI,[Dict_PseudoINCLUDEHEAD1::+0]
           MOV ECX,[Dict_PseudoINCLUDEHEAD1::+4]
    .F10:  REP MOVSB                                 ; Selected INCLUDE* pseudooperation.
           STOSB                                     ; Separating space.
           MOV AL,'"'
           STOSB                                     ; Leading quote.
           LEA ESI,[EBX+FILE.Name]
    .F15:  LODSB
           CMP AL,0
           JE .F20:
           CMP AL,'\'
           JNE .F17:
           MOV AL,'/'                                ; Convert backslash to slash for display only (in chunkResolved).
    .F17:  STOSB
           JMP .F15:
    .F20:  MOV AL,'"'                                ; Closing quote.
           STOSB
           MOV ESI,[%CiSubopBegin]
           MOV ECX,[%CiSubopEnd]
           SUB ECX,ESI
           REP MOVSB                                 ; Suboperation, if exists.
           MOV AL,13
           STOSB
           MOV AL,10                                 ; End of line.
           STOSB
         POP ESI
         MOV [ESI+CHUNK.Top],EDI                     ; chunkResolved is completed now.
         LEA ECX,[EBX+FILE.Name]
         SysOpenFileMap EBX,ECX                      ; Open the resolved file.
         XCHG ECX,ESI
         JC .E6915:                                  ; Error reading file "!1S".
         XCHG ESI,ECX
         MOV ECX,EAX                                 ; The file contents is now mapped at ESI,ECX.
         SetSt [EDX+STM.Flags],stmtAtLeast1
         ListStore [Src.FileList::],EBX              ; File EBX will be appended to Src.FileList.
         MOV EBX,EAX
         MOV ESI,[%CiPrevChunk]                      ; Resolved CHUNK.FilePtr will be updated with nonvolatile opened file.
         MOV [ESI+CHUNK.FilePtr],EAX
         JNSt [EDX+STM.Status],stm1,.F50:            ; Skip the check if include-only-once is not required.
         ; Check if file EBX has already been included in the current program.
         MOV ESI,[EDX+STM.Program]
         ; If the file EBX is the current source file, it is assumed already included in program,
         ;   though its not in Pgm.InclFilesTable yet.
         Invoke ChunkFilesCompare,EBX,Ea.SrcFile::
         JNE .F47:                                   ; If the included file differs from the main source, check other included files.
    .F46: ; Include-only-once (stm1) is set, file will not be included and chunkResolved will be marked as chunkSkipped.
         MOV EAX,[%CiPrevChunk]
         SetSt [EAX+CHUNK.Status],chunkSkipped
         MOV [EAX+CHUNK.FilePtr],EBX
         JMP .F80:
    .F47:MOV ECX,[ESI+PGM.InclFilesNr]
         MOV EDI,[ESI+PGM.InclFilesTable]            ; Points to ECX dwords with pointers to included files.
         JECXZ .F50:                                 ; Skip if the table is empty yet.
         MOV ESI,[ESI+PGM.InclFilesTable]            ; Points to ECX dwords with pointers to included files.
    .F48:LODSD
         Invoke ChunkFilesCompare,EBX,EAX
         JE .F46:                                    ; Do not include when stm1 is set and file was already included.
         LOOP .F48:
    .F50:; Create chunkIncluded (or chunkBin) with the contents of file EBX.
         MOV EAX,[%CiPrevChunk]                      ; Current chunkResolved.
         MOV [EAX+CHUNK.FilePtr],EBX
         ; Contents of file EBX will be included to a new chunk and then suboperated/subheaded.
         Invoke ChunkSplit,EAX,[EAX+CHUNK.Top]
         MOV [%CiPrevChunk],EAX
         MOV ECX,[%ReturnEBX]                        ; ^STM.
         JSt [ECX+STM.Status],stmIncludeBin,.F55:
         SetSt [EAX+CHUNK.Status],chunkIncluded
    .F55:JNSt [ECX+STM.Status],stmIncludeBin,.F60:
         SetSt [EAX+CHUNK.Status],chunkBin
    .F60:MOV [EAX+CHUNK.FilePtr],EBX
         MOV EDX,[EBX+FILE.BufPtr]
         MOV ECX,[EBX+FILE.BufSize]
         ADD ECX,EDX
         MOV [EAX+CHUNK.Bottom],EDX
         MOV [EAX+CHUNK.Top],ECX                     ; Chunk EAX now keeps the whole file contents. Chunk EAX will be suboperated.
         MOV EAX,[%CiSubopBegin]
    .F65:CMP EAX,[%CiSubopEnd]
         JE .F75:                                    ; Skip if no suboperations is requested.
         CMPB [EAX],'{'
         JE .F70:
         CMPB [EAX],'['
         JNE .E6912:                                 ; Unexpected text "!1S" following the file name.
    .F70:MOV [%CiSubopPtr],EAX
         Invoke ChunkSuboperate,[%CiPrevChunk],EAX,[%CiSubopEnd],msgSuppress
         JC .E6911:                                  ; Wrong file suboperation "!1S".
         JZ .F65:                                    ; If succesfully suboperated. EAX points to the next possible chained suboperation.
    .F75:; All suboperations were performed on chunk.
         MOV EDX,[%ReturnEBX]                        ; Original INCLUDE* statement.
         JNSt [EDX+STM.Status],stmIncludeHead,.F80:
         MOV EDI,[%CiPrevChunk]                      ; chunkIncluded
         Invoke EaBufferReserve::,ChunkInclude
         Invoke ChunkSubHead,EDI,EAX                 ; Constrain chunk contents to HEAD/ENDHEAD block(s).
         BufferRetrieve EAX
         PUSH EAX
           ; ESI,ECX now contains one or more HEAD..ENDHEAD chunks of file EBX source text. Or chunkError.
           ; The first chunk from buffer will replace chunkIncluded EDI, and if more chunks follow, EDI will be split.
           MOV EDX,SIZE# CHUNK
           CopyTo EDI,ESI,Size=EDX
    .F76:  ADD ESI,EDX
           SUB ECX,EDX
           JZ .F78:
           Invoke ChunkSplit,EDI,[EDI+CHUNK.Top]     ; Create new chunkInclude for the second HEAD..ENDHEAD block.
           MsgUnexpected cc=C
           MOV [%CiPrevChunk],EAX
           CopyTo EAX,ESI,Size=EDX
           JMP .F76:
    .F78:POP EAX
         Invoke EaBufferRelease::,EAX
    .F80:; No matter if file EBX was actually included or not, it will be recorded to program.InclFilesTable.
         MOV EDX,[%ReturnEBX]
         MOV ESI,[EDX+STM.Program]
         MOV EAX,[EDX+STM.LinePtr]                   ; EAX=LinePtr to INCLUDE* statement.
         MOV ECX,[ESI+PGM.InclFilesNr]
         ; Append file EBX, DosDateTime and LinePtr EAX to program files table. ECX is file index in table.
         MOV EDI,[ESI+PGM.InclFilesTable]
         MOV [EDI+4*ECX],EBX                         ; Free position in the table was already checked near start of .File.
         MOV EDI,[ESI+PGM.InclLinePtrTable]
         MOV [EDI+4*ECX],EAX
         MOV EDI,[ESI+PGM.InclFileTimeTable]
         MOV EAX,[%CiDosDateTime]
         MOV [EDI+4*ECX],EAX
         INC ECX
         MOV [ESI+PGM.InclFilesNr],ECX
         XOR EAX,EAX                                 ; Signalize no error on return from SysEachFile.File.
         JMP .F99:
    .E6911:MOV EDX,6911<<16+chunkError+chunkReplace  ; >> Wrong file suboperation "!1S".
      .e1: MOV ESI,[%CiSubopPtr]
           MOV ECX,[%CiSubopEnd]
           SUB ECX,ESI
           JMP ChunkInclude.Error:
    .E6912:MOV EDX,6912<<16+chunkError+chunkReplace  ; >> Unexpected text "!1S" following the file name.
           JMP .e1:
    .E6915:MOV EDX,6915<<16+chunkError               ; >> Error reading file "!1S".
     .e4:  MOV ECX,[%ReturnEBX]
           SetSt [ECX+STM.Flags],stmtAtLeast1        ; Prevent E6914 from appearing after E6915.
    .E_EDX:TEST ESI
           JZ .e5:
           CMPB [ESI],0
           JZ .e5:                                   ; If [ESI+FILE.Name] is not valid, use Filemask insted.
           GetLength$ ESI
           JMP ChunkInclude.Error:
      .e5: MOV ESI,[%CiFilemaskPtr]
           MOV ECX,[%CiFilemaskEnd]
           SUB ECX,ESI
           JMP ChunkInclude.Error:
    .E6916:MOV EDX,6916<<16+chunkError+chunkAbort    ; >> "!1S" - number of included files exceeded MaxInclusions.
           JMP .e4:
    .F99:  RET
      ENDP .File

    .Error PROC ; Insert chunkError at position [%CiPrevChunk].
         ; INCLUDE-Errors discovered in program pass 1 propagate to the final pass in a special chunkError.
         ; Input: EDX is CHUNK.Status, for instance 6911<<16+chunkError.
         ;        ESI is pointer to volatile msg parameter.
         ;        ECX is size of volatile msg parameter.
         ; Output:EDX=new chunk, EAX=new chunk status. ECX undefined.
         MOV EAX,[%CiPrevChunk]
         JSt EDX,chunkReplace,.30:                   ; If this chunkError will replace previous source chunk EAX.
         Invoke ChunkSplit,EAX,[EAX+CHUNK.Top]       ; Other errors insert new error chunk.
         MOV [%CiPrevChunk],EAX
    .30: MOV [EAX+CHUNK.Status],EDX
         MOV EDX,EAX
         JECXZ .60:                                  ; If parameter !1S is empty.
         PoolStore [Src::+SRC.Pool],ESI,ECX
         ; EAX,ECX=!1S stored permanently. In most cases it contains filename or suboperation.
    .60: ADD ECX,EAX
         MOV [EDX+CHUNK.Bottom],EAX
         MOV [EDX+CHUNK.Top],ECX
         PUSHAD
          MOV ESI,EAX
          MOV EDI,EAX
           SUB ECX,EAX
          JNA .70:
    .62:  LODSB
          CMP AL,'\'
          JNE .65:
          MOV AL,'/'
    .65:  STOSB
          LOOP .62:
    .70: POPAD
         MOV EAX,[EDX+CHUNK.Status]
         STC
         RET
       ENDP .Error

    .99:EndProcedure ChunkInclude

        ENDPROGRAM chunk

▲Back to the top▲