EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

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

Chunk represents a continuous area of memory-mapped source text. Chunks are kept on Src.ChunkList. 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 NOWARN=2101
chunk PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
INCLUDEHEAD "euroasm.htm"
INCLUDEHEAD  \  ; Include headers of another modules used in this module.
ctx.htm,     \
ea.htm,      \
eaopt.htm,   \
exp.htm,     \
lst.htm,     \
member.htm,  \
msg.htm,     \
pgm.htm,     \
pgmopt.htm,  \
src.htm,     \
stm.htm,     \
syswin.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).
.IdSize  D DWORD ; Size of HEAD block identifier, may be 0.
.LinePtr D DWORD ; Pointer to 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 which contains line specified by LinePtr into two chunks, so that LinePtr becomes both lower Chunk.Top and new higher Chunk.Bottom.
When LinePtr is 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 to 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
     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.
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 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 following program passes.
It 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 in FAT format.
CiIncFile     LocalVar Size=SIZE#FILE ; Included file. May be copied to Src.FileList later.
    ClearLocalVar
    MOV EBX,[%Stm]
    JSt [EBX+STM.CtxStatusAll],ctxNoEmit,.99:
    SetSt [Src.Lst::+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 operand to include. EBX is statement. EBP is stack frame.
               ; Output: EBX,EBP must be preserved.
     MOV EDX,EAX ; Size of included filename. It may contain path, wildcards, suboperations.
     StripSpaces EDI,EDX
     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.
.O4: Invoke EaoptGetOnePath::,[Ea::+EA.Eaopt.IncludePathPtr],[Ea::+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::+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 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,chunkE6916,.O9: ; Some errors should stop resolving further files.
     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
.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 volatile ^FILE: unopened %CiIncFile with assigned non-wildcarded name.
  ;        EDI=Pointer to WIN32_FIND_DATAW structure with file attributes.
  ;        [%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 EAX,[EDI+WIN32_FIND_DATAW.LastWriteTime]
     SysFileTimeToDosDateTime EAX                ; Included filetime is needed in OMF output format dependency record.
     MOV [%CiDosDateTime],EAX
     MOV EDX,[%ReturnEBX]                        ; ^STM with pseudoinstruction INCLUDE*.
     MOV ECX,[Src.Inclusions::]                  ; Number of already included files in this source.
     LEA ESI,[EBX+FILE.Name]                     ; For the case of E6916.
     INC ECX
     CMP ECX,[Ea::+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::+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:
       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 .Operand.SysEachFile.
     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
     MOV EAX,[EDX+CHUNK.Status]
     STC
     RET
   ENDP .Error

.99:EndProcedure ChunkInclude
    ENDPROGRAM chunk

▲Back to the top▲