Src represents one source file which is assembled by €ASM.
As there can be only one source being assembled at the moment, the object Src is defined statically in [.bss] segment.
Src has its own memory pool.
The input source file name and its suboperation are provided by Ea.SrcFile and Ea.SubPtr.
SrcAssemble is invoked by EaAssemble
with the source file name specified in Ea.SrcFile
. Path of source filename is also used to locate the local option file euroasm.ini
and to locate the listing file, which will have the same filename as source
but appended with extension .lst
.
src PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
INCLUDEHEAD euroasm.htm, \ Interface (structures, symbols and macros) of other modules used in this source.
chunk.htm,ctx.htm,dict.htm,ea.htm,eaopt.htm,exp.htm,lst.htm,msg.htm,pgm.htm, \
pgmopt.htm,reloc.htm,sss.htm,stm.htm,sym.htm,var.htm
src HEAD ; Start of module interface.
SRC STRUC ; +00h.
.FsSrcNamePtr D D ; Pointer to a string with default (envelope) program name derived from source filename.
.FsSrcNameSize D D ; Default program name size.
.Errorlevel D D ; Highest errorlevel reached in this source.
.Inclusions D D ; Number of succesfull INCLUDE* statements in this source.
; +10h.
.Pool D D ; Src's memory pool.
.CtxStack D D ; ^STACK of context.
.EaoptStack D D ; ^STACK of EAOPT maintained with EUROASM PUSH/POP.
.HeadStack D D ; ^STACK of CHUNK_HEAD for nested HEAD/ENDHEAD blocks.
; +20h.
.VarList D D ; ^LIST of %variables.
.ChunkList D D ; ^LIST of source chunks.
.FileList D D ; ^LIST of FILEs included in this source.
.PfList D D ; ^LIST of QWORDS (Ptr,Size) of output file names (used for detection of W3990).
; +30h.
.CurrentStm D D ; ^STM currently executed. Used in MsgProc.
.MemPeakPgm D D ; Memory allocated by Pgm.Pool.
.Lst DS LST ; Listing object.
.Eaopt DS EAOPT ; Current €ASM options in charge.
.Pgmopt DS PGMOPT ; Current program options used as default.
.IniFile DS FILE ; Local option file.
.LstFile DS FILE ; Listing file.
ENDSTRUC SRC
ENDHEAD src ; End of module interface.
[.bss] Src:: DS SRC ; The source object. [.text]
euroasm.ini, if found.
SrcCreate:: Procedure
Chunk LocalVar Size=SIZE#CHUNK
FileName$ LocalVar Size=MAX_PATH_SIZE ; Room for local euroasm.ini filename.
; Initialize memory pool.
Clear Src
MOV EDI,%EaPoolSize
PoolCreate Size=EDI, ErrorHandler=EaMallocError::
JC .90:
MOV [Src.Pool],EAX
LEA EBX,[Ea.SrcFile::]
LEA EDI,[EBX+FILE.Name]
CMPW [EDI],'./'
JNE .10:
LEA ESI,[EDI+2]
MOV ECX,SIZE#FILE.Name-2
REP MOVSB
.10: ; Derive default program name Src.FsSrcName from the source filename.
LEA ESI,[EBX+FILE.Name]
FileNameParse ESI
MOV ESI,EAX
SUB ECX,ESI
; ESI,ECX is now source file name without path and extension.
Invoke EaBufferReserve::,SrcCreate
Invoke EaFs2Id::,ESI,ECX,EAX ; Create an identifier from file name.
BufferRetrieve EAX
Invoke EaBufferRelease::,EAX
PoolStore [Src.Pool],ESI,ECX ; Permanently store derived name ESI,ECX.
MOV [Src.FsSrcNamePtr],EAX ; Will be used as default (envelope) program name.
MOV [Src.FsSrcNameSize],ECX
; Create memory structures of the source on source pool.
MOV EDX,[Src.Pool]
StackCreate EDX,SIZE#CTX,Depth=32
MOV [Src.CtxStack],EAX
StackCreate EDX,SIZE#EAOPT,Depth=4
MOV [Src.EaoptStack],EAX
StackCreate EDX,SIZE#CHUNK_HEAD,Depth=4
MOV [Src.HeadStack],EAX
ListCreate EDX,SIZE#VAR
MOV [Src.VarList],EAX
ListCreate EDX,8
MOV [Src.PfList],EAX ; Output file names are kept here.
ListCreate EDX,SIZE#CHUNK
MOV [Src.ChunkList],EAX
ListCreate EDX,SIZE#FILE
MOV [Src.FileList],EAX ; Included files are kept in Src.FileList.
Invoke LstCreate:: ; Initialize Src.Lst object.
; Three chunks will initialize assembly:
; 1. "FsSrcName: PROGRAM" stored on Src.Pool (chunkEnvelope).
; 2. contents of base source file mapped in memory (chunkSource).
; 3. "ENDPROGRAM FsSrcName:" stored in Src.Pool (chunkEnvelope).
; First create chunk Nr.1:
LEA EBX,[%Chunk]
LEA EAX,[Ea.SrcFile::]
MOV [EBX+CHUNK.FilePtr],EAX ; The main source.
MOV ECX,[Src.FsSrcNameSize] ; Create chunk contents on Src.Pool.
LEA EDX,[ECX+11]
PoolNew [Src.Pool],EDX, Align=BYTE
MOV EDI,EAX ; Room for envelope PROGRAM statement.
MOV [EBX+CHUNK.Bottom],EAX
MOV ESI,[Src.FsSrcNamePtr]
REP MOVSB
MOV AX,": "
STOSW
MOV ESI,[Dict_PseudoPROGRAM:: + DICT.Ptr] ; The text "PROGRAM".
MOV ECX,[Dict_PseudoPROGRAM:: + DICT.Size]
REP MOVSB
MOV AX,0x0A0D ; CR+LF.
STOSW
MOV [EBX+CHUNK.Top],EDI
MOVD [EBX+CHUNK.Status],chunkEnvelope
ListStore [Src.ChunkList],EBX
; Create chunk Nr.2:
LEA EBX,[%Chunk]
MOV EAX,[EBX+CHUNK.FilePtr]
LEA EDX,[EAX+FILE.Name] ; "./source.asm" instead of "source.asm" in Linux version.
CMPW [EDX],'./'
JNE .20:
INC EDX,EDX
.20: SysOpenFileMap EAX,EDX
.30: Msg cc=C,'8030',EDX ; Error reading source !1$.
JC .90:
; The main source file contents is now mapped in memory ESI,EAX.
MOV ECX,[ESI]
AND ECX,0x00FFFFFF ; Mask off the 4th byte.
CMP ECX,0x00BFBBEF ; Is it UTF-8 BOM?
JNE .40:
ADD ESI,3 ; Ignore BOM.
SUB EAX,3
JBE .30: ; Source file is too short.
.40: MOV [EBX+CHUNK.Bottom],ESI
ADD EAX,ESI
MOV [EBX+CHUNK.Top],EAX
MOVD [EBX+CHUNK.Status],chunkSource
; Source file might have been specified with suboperation, e.g. "source.asm"{1..%&-80}[10..99]
MOV ECX,[Ea.SubSize::]
JECXZ .60: ; If no suboperation requested.
MOV EAX,[Ea.SubPtr::]
ADD ECX,EAX ; EAX..ECX now specifies the string, say {1..%&-80}[10..99]
.50: Invoke ChunkSuboperate::,EBX,EAX,ECX,1
JC .90:
JZ .50: ; If chunk was suboperated, more chained suboperations may follow.
.60: ListStore [Src.ChunkList],EBX
MOV ECX,[Src.FsSrcNameSize] ; Now create chunk Nr.3:
LEA EDX,[ECX+13]
PoolNew [Src.Pool],EDX, Align=BYTE
MOV EDI,EAX
MOV [EBX+CHUNK.Bottom],EAX
MOV ESI,[Dict_PseudoENDPROGRAM:: + DICT.Ptr] ; The text "ENDPROGRAM".
MOV ECX,[Dict_PseudoENDPROGRAM:: + DICT.Size] ; 9.
REP MOVSB
MOV AL,' '
STOSB
MOV ESI,[Src.FsSrcNamePtr]
MOV ECX,[Src.FsSrcNameSize]
REP MOVSB
MOV AL,':'
STOSB
MOV AX,0x0A0D ; CR+LF.
STOSW
MOV [EBX+CHUNK.Top],EDI
MOVD [EBX+CHUNK.Status],chunkEnvelope
ListStore [Src.ChunkList],EBX
; Current options are inherited from the parent object (Ea).
CopyTo Src.Eaopt, Ea.EaoptIni::
CopyTo Src.Pgmopt, Ea.Pgmopt::
; Then the options will be updated from local euroasm.ini file, if found.
MOV ESI,Ea.SrcFile.Name::
MOV ECX,[Ea.SrcFile.NameOffs::]
LEA EDI,[%FileName$]
PUSH EDI
REP MOVSB ; Concantenate source path and "euroasm.ini".
MOV ESI,=B"euroasm.ini"
MOV ECX,12
REP MOVSB
POP EDI
MOV EDX,Src.IniFile
SysOpenFileMap EDX,EDI
Msg cc=C,'0160',EDI ; Local option file "!1$" was not found.
JC .70:
SetSt [EDX+FILE.Status],fileStFound
Msg '0170',EDI ; Assembling local option file "!1S".
ADD EAX,ESI
Invoke EaIniAssemble:: ,ESI,EAX, Src.Eaopt, Src.Pgmopt
SysCloseFile EDX
.70: CopyTo Ea.Eaopt::,Src.Eaopt,Size=SIZE#EAOPT
Msg '0180',Ea.SrcFile.Name, Ea.SubPtr:: ; Assembling source file "!1$"!2S.
.90: EndProcedure SrcCreate
SrcDestroy Procedure
LstSize LocalVar
Invoke LstGetFileName::
LEA EBX,[Src.LstFile]
SysAssignFile EBX,ESI,ECX
FileMkDir EBX
JNC .30:
.LstError:
LEA EAX,[EBX+FILE.Name]
Msg '7982',EAX ; Error writing to listing file "!1$".
JMP .40:
SrcDestroy.WriteStreamBlock PROC ; Callback from StreamDump macro.
ADD [%LstSize],ECX
FileWrite Src.LstFile,ESI,ECX
RET
ENDP SrcDestroy.WriteStreamBlock
.30: FileCreate EBX ; Write listing to ListFile.
JC .LstError:
MOVD [%LstSize],0 ; File size counter.
LEA EDX,[Src.Lst]
StreamDump [EDX+LST.Stream], .WriteStreamBlock
JC .LstError:
LEA EAX,[EBX+FILE.Name]
Msg '0860',EAX,[%LstSize] ; Listing file "!1$" created, size=!2D.
.40: FileClose EBX ; Close the listing file.
.50: CopyTo Ea.Eaopt::, Src.Eaopt, Size=SIZE#EAOPT ; Ea.Eaopt is updated from Src.Eaopt.
ListGetLast [Src.FileList]
JZ .80:
.70: SysCloseFile EAX ; Close all included files.
ListGetPrev EAX
JNZ .70:
SysCloseFile Ea.SrcFile:: ; Close the base source file.
.80: MOV EDX,[Src.Pool]
MOV [Src.CtxStack],0
PoolDestroy EDX
Msg cc=C,'2575','Src',EDX ; Deallocation of virtual memory !1C.Pool !2Hh failed.
ADD EAX,[Src.MemPeakPgm]
MOV [%ReturnEAX],EAX
.90: Clear Src
EndProcedure SrcDestroy
chunkBin or
chunkError, the returned data ESI..EAX is not source line
but binary data or Msg parameter. The caller should test chunk status first.
SrcFetchLine Procedure LinePtr, ChunkPtr
MOV EAX,[%ChunkPtr]
MOV EDI,[%LinePtr]
TEST EAX
JNZ .10:
ListGetFirst [Src.ChunkList]
.10: JZ .EndSource: ; No more chunks.
TEST EDI
JNZ .20:
MOV EDI,[EAX+CHUNK.Bottom]
.20: CMP EDI,[EAX+CHUNK.Bottom]
JB .30:
CMP EDI,[EAX+CHUNK.Top]
JB .50:
JE .40:
.30: ListGetNext EAX
JMP .10
.40: ListGetNext EAX ; TxtPtr was at the top of current chunk, lets start at the bottom of the next one.
JZ .EndSource:
MOV EDI,[EAX+CHUNK.Bottom]
.50: MOV [%ReturnEDX],EAX ; EAX=actual chunk; EDI=start of physical line
MOV ECX,[EAX+CHUNK.Top]
MOV ESI,EDI
SUB ECX,EDI
JZ .40:
JNSt [EAX+CHUNK.Status],chunkBin|chunkError,.60:
MOV ECX,[EAX+CHUNK.Top] ; Chunk EAX does not contain source lines but binary data or Msg parameter.
MOV [%ReturnESI],EDI
MOV [%ReturnEAX],ECX
JMP .NormalOutput:
.60: MOV AL,10
REPNE SCASB ; Find the end of physical line.
MOV [%ReturnESI],ESI
MOV [%ReturnEAX],EDI
MOV ECX,EDI ; Physical line ESI..EDI is specified. Now strip off comments.
MOV EDI,ESI
SUB ECX,ESI
.70: LODSB ; Skip leading white spaces in the line of text.
DEC ECX
JZ .NormalOutput:
ExpClassify AL
TEST AH,expWhiteSpace
JNZ .70:
CMP AL,'<' ;>
JE .CommentOnly:
CMP AL,'|'
JNE .NormalOutput:
MOV EDI,ESI
REPNE SCASB
MOV [%ReturnESI],EDI
JE .NormalOutput:
.CommentOnly:
MOV AH,flagZ ; ZF=1, CF=0
JMPS .90:
.EndSource:
MOV AH,flagZ+flagC ; ZF=CF=1
JMPS .90:
.NormalOutput:
MOV AH,0 ; ZF=CF=0
.90: SAHF
EndProcedure SrcFetchLine
SrcAssemble:: Procedure
Stm LocalVar Size=SIZE#STM ; Stm object used for statement parsing.
LEA EBX,[%Stm]
Invoke StmCreate::,EBX
SUB EDX,EDX
SUB EAX,EAX ; LinePtr=0, start with the 1st line of source, which is envelope "file: PROGRAM".
.10: ; Here is the main loop which will read and execute all statements in the source.
Invoke StmClean::,EBX ; Empty statement buffers and erase remains of previous statement.
MOV [Src.CurrentStm],EBX
Invoke StmParse::,EBX,EAX,EDX ; Fetch and parse statement adressed with EAX.
JC .60: ; If no more source lines.
INCD [Ea.StmCount::] ; Counter of total assembled statements.
JNSt [Ea.Eaopt.Status::],eaoptDISPLAYSTM,.18:
Invoke StmDisplayParsed::,EBX ; Diagnostic info how was the statement parsed.
.18:Invoke StmExecute::,EBX ; Execute the statement: fill statement buffers, modify context stack, create symbols etc.
XOR ECX,ECX
XOR EAX,EAX
.20:Invoke CtxPeek::,ctxPROGRAM,EAX ; Find emitting program context.
JC .30: ; Skip when envelope ENDPROGRAM was just executed. No more programs on stack.
MOV ECX,[EAX+CTX.ObjPtr] ; ECX=0 when PROGRAM statement was in NoEmit state.
JECXZ .20: ; If NoEmit, search the stack deeper.
JNSt [ECX+PGM.Status],pgmLastPass,.50: ; Skip the listing in nonlast passes.
JSt [ECX+PGM.Status],pgmLastJustSet,.40:
.30: Invoke StmListing::,EBX ; Create a listing line(s) and write to Lst.Stream.
.40: JECXZ .50:
RstSt [ECX+PGM.Status],pgmLastJustSet
.50: RstSt [Src.Lst.Status],lstVolMask ; Clear used volatile flags of listing.
Invoke StmFlush::,EBX ; Flush STM.EmitBuffer to the current section's SSS.EmitBuffer.
CMP [Src.Errorlevel],8
JAE .80: ; Abort if source-fatal error. The source file will be prematurely abandoned.
MOV EAX,[EBX+STM.LineNext] ; The next statement where to continue.
MOV EDX,[EBX+STM.ChunkNext]
TEST EAX
JNZ .10: ; If STM.LineNext is specified, go to assemble this new statement (macro or repeat block).
; Otherwise continue with the next statement in the normal source flow.
MOV EAX,[EBX+STM.LineEnd] ; End of just executed statement is the beginning of the next one.
MOV EDX,[EBX+STM.ChunkPtr]
JMP .10:
.60: ; End of source text.
StackPop [Src.CtxStack] ; Check if no context was left on stack.
JC .80: ; It should be empty.
MOV EDI,EAX ; Some block in source was left open, this is an error.
Invoke CtxGetEndTypename::,[EDI+CTX.Status] ; EAX=^DQ Ptr,Size
JNSt [EDI+CTX.Status],ctxREPEAT,.70:
JSt [EDI+CTX.Status],ctxExited,.70:
XCHG EAX,EDI ; Operation %REPEAT has block identifier in %1 instead of label field.
.70: Msg '7110',EAX,EDI ; Wrong nesting, expected !1S !2S. ENDblock id
JMP .60:
.80: Invoke StmDestroy::,EBX
MOVD [Src.CurrentStm],0
.90: EndProcedure SrcAssemble::
SrcPosition Procedure LinePtr
MOV EBX,[%LinePtr]
MOV EAX,Ea.SrcFile::
CALL .TryFileEAX:
JNC .Found:
ListGetFirst [Src.FileList]
JZ .20:
.10:CALL .TryFileEAX:
JNC .Found:
ListGetNext EAX
JNZ .10:
.20:MOV EAX,Ea.IniFile:: ; Try global "euroasm.ini".
CALL .TryFileEAX:
JNC .Found:
MOV EAX,Src.IniFile ; Try local "euroasm.ini".
CALL .TryFileEAX:
MOV EDI,=B(0) ; No source file found.
SUB ESI,ESI
MOV [%ReturnEDX],ESI
STC
JMP .90:
SrcPosition.TryFileEAX: PROC ; Check if LinePtr=EBX is in the mapped range of file EAX.
; Output: CF=1 if not in range. ECX=?
MOV ECX,[EAX+FILE.BufPtr] ; Bottom of mapped source.
CMP EBX,ECX
JB .T9:
ADD ECX,[EAX+FILE.BufSize] ; Top of mapped source.
CMP EBX,ECX ; Compare EBX=LinePtr with the top of source.
CMC
.T9:RET
ENDP SrcPosition.TryFileEAX:
.Found:
MOV EDX,EAX ; ^FILE.
MOV [%ReturnEDX],EAX ; ^FILE.
MOV EDI,[EDX+FILE.BufPtr]
MOV ECX,[EDX+FILE.BufSize]
SUB ESI,ESI ; Line counter.
MOV AL,10 ; End of physical line.
.60:INC ESI
JECXZ .80:
REPNE SCASB ; Find EOL.
CMP EBX,EDI ; Match with LinePtr?
JAE .60:
.80:LEA EDI,[EDX+FILE.Name]
ADD EDI,[EDX+FILE.NameOffs] ; Skip file path, if any. CF=0.
.90:MOV [%ReturnEDI],EDI
MOV [%ReturnEAX],ESI
EndProcedure SrcPosition
ENDPROGRAM src