flat assembler
Message board for the users of flat assembler.

Index > Main > include resolution process in famsg

Author
Thread Post new topic Reply to topic
fabbel



Joined: 30 Oct 2012
Posts: 114
fabbel 06 Oct 2025, 12:04
Hi Tomasz,
Am working on extending include features via macro / calm
* typ., my own version of "include once"
* but also "deferred exec" of block of instructions at the end of current include file ( as opposed to at end of script as done via postpone)

in the process, the below questions/topics arised:
1/
... unless mistaken, I see no explicit details of the include resolution process in fasmg doc...
as I read in fasmg source, I understand it normally is: try source file directory first, then %include% env variable (if it exists).
Can u pls confirm / elaborate ? ... maybe worth putting in the manual ?

2/
while playing with postpone for inspiration / see how to collaborate with it, I came to experience behavior of postpone that I was not expecting (but came to like / cope with) :
I initially thought pospone block would only care for "regular" macro inx / fasmg directives inside postpone ... end postpone block - seeing it as a king of "specialized macro"...
... then below sample puzzled me for some time..
Code:
include 'utility/xcalm.inc'

calminstruction postpone
                display '*** postpone override' bappend 0x0A0D

                asm postpone

                display '*** executing postpone' bappend 0x0A0D
end calminstruction

postpone
        display '...  some postponed block of inx here', 13, 10
end postpone

display '... script body here ...', 13, 10
    


=> this yields below ouput:
Quote:

flat assembler version g.ktge
*** postpone override
*** executing postpone
... script body here ...
*** executing postpone
... some postponed block of inx here


... which is to say, when executing postpone block at the end of script, it appears to be calling back into postpone calm override, in the middle of calm inx, just after the line
asm postpone
... while i would initially have expected the '*** executing postpone' msg to display only once, at the time the postpone block is set up (just like '*** postpone override' msg).
Again, i finally manage and appreciate this behavior, but i wonder if it wouldn't deserve bit more explicit description
Post 06 Oct 2025, 12:04
View user's profile Send private message Reply with quote
fabbel



Joined: 30 Oct 2012
Posts: 114
fabbel 06 Oct 2025, 12:18
.. another was to put it: **any** code that is 'installed' after pospone directive will get executed at end of script, be it regular fasmg (macro) code / directives, or even alm code (even if part of enclosing calm inx)
Post 06 Oct 2025, 12:18
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8461
Location: Kraków, Poland
Tomasz Grysztar 06 Oct 2025, 14:28
fabbel wrote:
1/
... unless mistaken, I see no explicit details of the include resolution process in fasmg doc...
as I read in fasmg source, I understand it normally is: try source file directory first, then %include% env variable (if it exists).
Can u pls confirm / elaborate ? ... maybe worth putting in the manual ?
I thought this should be compatible with fasm 1, which has it documented as follows:
flat assembler manual wrote:
The file is first searched for in the directory containing file which included it and when it is not found there, the search is continued in the directories specified in the environment variable called INCLUDE (the multiple paths separated with semicolons can be defined there, they will be searched in the same order as specified). If file was not found in any of these places, preprocessor looks for it in the directory containing the main source file (the one specified in command line).
But it seems I forgot to ensure that fasmg is compatible, and unlike fasm 1 it also checks current directory before paths from the environment variable. While this is normally not very noticeable difference, I should at least document it properly, as you noted.

fabbel wrote:
while playing with postpone for inspiration / see how to collaborate with it, I came to experience behavior of postpone that I was not expecting (but came to like / cope with) :
I initially thought pospone block would only care for "regular" macro inx / fasmg directives inside postpone ... end postpone block - seeing it as a king of "specialized macro"...
POSTPONE has been implemented as a control directive (and is defined as such by the manual), so it controls the flow of the assembly in a similar way to other control directives like WHILE.

I recall I briefly experimented with POSTPONE being a single-use macro (similarly to how it was implemented in the preprocessor of fasm 1), but implementing it as a control directive felt simpler and more elegant.
Post 06 Oct 2025, 14:28
View user's profile Send private message Visit poster's website Reply with quote
fabbel



Joined: 30 Oct 2012
Posts: 114
fabbel 06 Oct 2025, 15:29
thx for feedback !
Regarding postpone, indeed, it is defined as a control directive in the manual... it is just that I initially didn't anticipate this kind of effect (even if now appreciate like it is )

... just wanted to share experience really
... i mean control directives are - for me - associated with "regular code"
(as opposed to alm code, mean typ. directive like 'repeat/end repeat', 'if/end if', ... cannot normally show up inside calm inx impl.,
while, conversly, alm can only appear between calminstruction... end calminstruction)
... so it just confused my understanding as I initially just didn't not expect postponed execution - even if directive - to be able to call/jump directly inside alm code w/o explicit switch to alm
... at some point while experimenting, I even thought that at end of script, postpone was starting again from initial postpone statement, thus re-executing the whole postpone override code again
... before I realized it is really the "intrinsic" postpone directive itself that is setting the point of exec at end of script - no matter what/where that is....
Post 06 Oct 2025, 15:29
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8461
Location: Kraków, Poland
Tomasz Grysztar 06 Oct 2025, 18:11
The state of fasmg's control flow can be stored and resumed at any point. This is essential for things like CALM code calling a macro, which in turn may call another CALM instruction and so on. A simple example with REPEAT (another control directive) demonstrates that everything can be rewound:
Code:
calminstruction demo
        asm repeat 3
        display '!'
end calminstruction

demo
end repeat    
Unless the counter reached the limit, END REPEAT restores the source context saved by REPEAT (it's a SourceContext structure in fasmg's internals), which points the assembler back to the CALM code inside "demo".

What makes it especially tricky is the fact that CALM commands like DISPLAY do not obey the assembly state which for normal (textual) instructions determines whether to execute only the unconditional ones (like when REPEAT has 0 count, or IF has a false condition, or POSTPONE is executed during the normal assembly pass, which also puts it into this "skip block" mode). It makes these command essentially like unconditional instructions and this is what caused the message to show up twice in your example.
Post 06 Oct 2025, 18:11
View user's profile Send private message Visit poster's website Reply with quote
fabbel



Joined: 30 Oct 2012
Posts: 114
fabbel 08 Oct 2025, 11:40
thx vm for that example !
... striking / tricky stuff indeed... but makes sense vs what I saw with postpone
... though imho, and maybe esp. bec such interactions betw calm and fasmg directives are indeed tricky, am still wondering, if this should not be better documented somehow
... in the manual, or maybe a note in your " Tricky stuff in fasmg" thread ... let u decide ofc - as u see fit
... typ. as in our in your example, you end up being able to control a calm loop using fasmg repeat directive i/o of the 'more normal' way inside calm like
Code:
compute _idx, 1
compute _max, 3
(...)

@loop:
(...)

compute _idx, _idx + 1
check _idx > _max
jno @loop
    



reg.
Quote:

This is essential for things like CALM code calling a macro, which in turn may call another CALM instruction and so on

=> I see your point and that makes perfect sense indeed.. but as I see, this is still mostly calling sub-macro / sub calm inx as 'complete' entities ...
... I mean, when I look at your demo example, and even if I understand it now, it still feels like a struggle in my mind, not to question
* is the display '!' running once / atomic with execution of calminstruction demo ?
* or is it actually taken 'unconditionally' as part of the generated repeat / end repeat block ?
Post 08 Oct 2025, 11:40
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8461
Location: Kraków, Poland
Tomasz Grysztar 08 Oct 2025, 13:16
Let me try to write down an explanation, I need something I could then use as a basis for additions to the documentation.

When a source text is assembled, every consecutive line is preprocessed and executed, and the execution of line's instruction is conditional, depending whether we are in "skip" mode, macro definition mode, or a regular assembly. Unconditional instructions are executed regardless of the mode.

This applies also to macros. When a macro is called, every line from the text of that macro is preprocessed (with the fresh values of the local parameters) and executed.

A control directive like REPEAT or WHILE (and also POSTPONE) is able to store the "source context", which points to a line in a macro or source file, and all its "parent lines" (the line that called this macro or included this source file, and so on). This allows to repeat the assembly of the block of source by simply rewinding the assembler back to where it were when the REPEAT block was first started. And if the block should not be assembled at all (when REPEAT has zero count or WHILE's condition is already false), the assembler is put into "skip mode".

Now a CALM instruction is a special kind of macro, where instead of standard preprocessing there is an execution of CALM commands, and the commands may generate lines to be assembled. These lines are executed normally, just like if a regular macro generated them, but if the "source context" is stored, it remembers the place in the CALM code, the position of a command that assembled the line. If we rewind to this context, we are going to execute the remaining code inside the CALM instruction once more. And because CALM commands (the binary code, not ALM source) are always executed unconditionally, they are not affected by the "skip" mode. Thus after POSTPONE they are executed during the initial skipping of the block, but then also when the block gets actually assembled again.
Post 08 Oct 2025, 13:16
View user's profile Send private message Visit poster's website Reply with quote
fabbel



Joined: 30 Oct 2012
Posts: 114
fabbel 08 Oct 2025, 13:36
... thx again & all clear now ! much appreciated !
... your last 2 posts may very well be published as some 'official' documentation as I was suggesting ...
Post 08 Oct 2025, 13:36
View user's profile Send private message Reply with quote
fabbel



Joined: 30 Oct 2012
Posts: 114
fabbel 08 Oct 2025, 13:45
.. last post sounds pretty good as the general explanation + prev one to illustrate with example
Post 08 Oct 2025, 13:45
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4294
Location: vpcmpistri
bitRAKE 09 Oct 2025, 04:32
Tomasz Grysztar wrote:
I thought this should be compatible with fasm 1, which has it documented as follows:
flat assembler manual wrote:
The file is first searched for in the directory containing file which included it and when it is not found there, the search is continued in the directories specified in the environment variable called INCLUDE (the multiple paths separated with semicolons can be defined there, they will be searched in the same order as specified). If file was not found in any of these places, preprocessor looks for it in the directory containing the main source file (the one specified in command line).
But it seems I forgot to ensure that fasmg is compatible, and unlike fasm 1 it also checks current directory before paths from the environment variable. While this is normally not very noticeable difference, I should at least document it properly, as you noted.
I tried to put this to the test in the latest fasmg:
Code:
@echo off
REM     include_order.cmd -- Test include path search order:
REM     (assumes fasmg in environment)
setlocal
mkdir io_file
mkdir io_cwd
mkdir io_env

REM __SOURCE__
echo include 'io_file/test.inc' >test.asm
REM __FILE__
echo include 'test_path.inc' >io_file/test.inc

echo display '1' >io_file/test_path.inc
echo display '2' >io_cwd/test_path.inc
echo display '3' >io_env/test_path.inc
echo display '4' >test_path.inc


REM     1. __FILE__ path superceeds all others
set "include=%~dp0io_env"
pushd io_cwd
fasmg -n ../test.asm
del ..\io_file\test_path.inc
pause

REM     2. current working directory
fasmg -n ../test.asm
del test_path.inc
pause

REM     3. %include% environment variable paths
fasmg -n ../test.asm
set "include="
pause

REM     4. __SOURCE__ path, command line file location
fasmg -n ../test.asm
popd
del test
del test.asm
del test_path.inc
del io_file\test.inc
del io_env\test_path.inc
rmdir io_file
rmdir io_cwd
rmdir io_env
endlocal    
... and it doesn't seem to check the "main source file location" or perhaps I am misunderstanding?

_________________
¯\(°_o)/¯ AI may [not] have aided with the above reply.
Post 09 Oct 2025, 04:32
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8461
Location: Kraków, Poland
Tomasz Grysztar 09 Oct 2025, 05:27
That's the difference from fasm, it checks the current directory (the one from which they process was started, for example from command line).
Post 09 Oct 2025, 05:27
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4294
Location: vpcmpistri
bitRAKE 09 Oct 2025, 05:53
Yeah, that's clear, but your use of "also" means "in addition to"; but the reality is "instead of". The test makes the difference explicit.
Post 09 Oct 2025, 05:53
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8461
Location: Kraków, Poland
Tomasz Grysztar 09 Oct 2025, 06:02
It's not "instead of", because fasm did not have any additional path there in the order before ones from the environment variable. That's why I phrased it this way.

I did not add that the other difference is that it does not do the final fallback like fasm, but that was just my quick initial response. I then wrote it down properly in fasmg's manual.
Post 09 Oct 2025, 06:02
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4294
Location: vpcmpistri
bitRAKE 09 Oct 2025, 06:13
Quote:
If file was not found in any of these places, preprocessor looks for it in the directory containing the main source file (the one specified in command line).
That doesn't happen on my system.
Post 09 Oct 2025, 06:13
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8461
Location: Kraków, Poland
Tomasz Grysztar 09 Oct 2025, 06:16
bitRAKE wrote:
Quote:
If file was not found in any of these places, preprocessor looks for it in the directory containing the main source file (the one specified in command line).
That doesn't happen on my system.
You're quoting fasm's manual, not fasmg's. This the "final fallback" I referred to in my previous reply, and fasmg does not have it.
Post 09 Oct 2025, 06:16
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4294
Location: vpcmpistri
bitRAKE 09 Oct 2025, 06:29
It's easy enough to run the test on fasm:
Code:
flat assembler  version 1.73.32  (1048576 kilobytes memory)
1
1 passes, 0 bytes.
Press any key to continue . . .
flat assembler  version 1.73.32  (1048576 kilobytes memory)
3
1 passes, 0 bytes.
Press any key to continue . . .
flat assembler  version 1.73.32  (1048576 kilobytes memory)
3
1 passes, 0 bytes.
Press any key to continue . . .
flat assembler  version 1.73.32  (1048576 kilobytes memory)
../io_file/test.inc [1]:
include 'test_path.inc'
error: file not found.    
Post 09 Oct 2025, 06:29
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8461
Location: Kraków, Poland
Tomasz Grysztar 09 Oct 2025, 06:48
Oh, I though that you were still referring to fasmg. I did not check whether fasm behaves according to its own documentation, I was focused on fasmg here.

But even if fasm checks current directory, it is still not the same as fasmg, because it does it after checking the environment, not before.

I think at one point I deemed being able to override environment headers with current directory more useful than having it only be the last resort. Should I backport this decision to fasm?
Post 09 Oct 2025, 06:48
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4294
Location: vpcmpistri
bitRAKE 09 Oct 2025, 07:47
Tomasz Grysztar wrote:
I think at one point I deemed being able to override environment headers with current directory more useful than having it only be the last resort. Should I backport this decision to fasm?
My perspective seems so niche - honestly have no desire to impose it on anyone. What I would say is that my terminal usage has the current directory as less important than the __SOURCE__, but that could be my style - I've not put in the work to understand the other perspective. __SOURCE__ is accessible within the code, but the working directory isn't - it does seem like added flexibility. __FILE__ gets the job done. %INCLUDE% is high-level flexibility. Is CWD fine-grain flexibility? Perhaps I've convinced myself.

_________________
¯\(°_o)/¯ AI may [not] have aided with the above reply.
Post 09 Oct 2025, 07:47
View user's profile Send private message Visit poster's website Reply with quote
fabbel



Joined: 30 Oct 2012
Posts: 114
fabbel 13 Oct 2025, 13:40
Hi again, sry in advance for being picky again maybe... but just thinking file directive may also need some similar extra documentation... or does it always require full path to target ?
(... btw, unless mistaken this wasn't specified in fasm1 doc either...)
Post 13 Oct 2025, 13:40
View user's profile Send private message Reply with quote
fabbel



Joined: 30 Oct 2012
Posts: 114
fabbel 13 Oct 2025, 14:22
.. actually, by a second look at current doc + quick look at famsg source code, i kinda understand it does similar file lookup as done by include directive - except the %include% env variable part... Am I correct ?
Post 13 Oct 2025, 14:22
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.