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 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
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
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]
Chunk.Top
and new higher Chunk.Bottom
.
.FilePtr
and flag chunkIncluded
.
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 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 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
chunkInclude
and the chunk's .Bottom and .Top is restrained to HEAD/ENDHEAD block.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 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
stmIncludeBin
, stmIncludeHead
and/or
stm1
specifying which operation was the handler invoked from.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
).
└───────────────────┘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 statementINCLUDE* 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 originalINCLUDE*
pseudoinstruction. ; [%CiOrigChunk] points to chunkOrig with originalINCLUDE*
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 containsINCLUDE* 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 statementINCLUDE* "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 instance6911<<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