flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > REPT for conditional preprocessing

Author
Thread Post new topic Reply to topic
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 20 Jan 2020, 12:34
Imagine you want to implement a macro that defines symbolic constants and macros depending on some “numerical” conditions. Say, you have some numeric value and depending on its value (on which range it falls into) you would later use one data definition directive or another, or even completely change the layout of data being generated. Such cases are quite rare but still happen sometimes.

The only preprocessor block that supports numeric values is rept. The trick then is to write an expression based on symbolic constants that would either become 1 or 0. If relying on FASM’s internal value representation is not a problem, one could even implement macros like these:
Code:
macro if_le x*, c*, action&
{
  rept (((x) - (c) - 1) and (1 shl 63)) shr 63 \{ action \}
}

macro if_l x*, c*, action&
{
  rept (((x) - (c)) and (1 shl 63)) shr 63 \{ action \}
}

macro if_ge x*, c*, action&
{
  rept (((c) - (x) - 1) and (1 shl 63)) shr 63 \{ action \}
}

macro if_g x*, c*, action&
{
  rept (((c) - (x)) and (1 shl 63)) shr 63 \{ action \}
}    
and then use them as follows:
Code:
y equ abc
if_g x, 123'456, y equ 8    
which is the moral equivalent of
Code:
; Warning: This code won’t work!
y equ abc
if x > 123'456
  y equ 8
end if    
except that is wouldn’t work since assembler directives are processed after preprocessor ones.

Suggested macros only work for one-liners and it’s quite tricky if even possible to adjust them for code blocks. Besides, the bit-twiddling looks quite dirty and is subject to break if FASM’s internals change.

1. Is the limitation of only using numeric expressions in rept by design or just for the purpose of abuse prevention?
2. Why were the comparison operators defined as having non-numeric results while still allowing numeric expressions as if conditions?
3. (Possible suggestion) It might be a good idea (or not) to extend rept capabilities by making the expression treatment the same as in if directive.
Post 20 Jan 2020, 12:34
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20489
Location: In your JS exploiting you and your system
revolution 20 Jan 2020, 12:52
You can extend it to any arbitrary block length by not closing the rept:
Code:
macro if_le x*, c*, action&
{
  rept (((x) - (c) - 1) and (1 shl 63)) shr 63 \{ action ; <--- not closed
}
x equ 1
y equ 0
if_le 1, 2
 x equ 2
 y equ 4
} ;<--- close the rept here

;or for a one liner
if_le 1,2, x equ 4 }    
BTW: Untested. This is from memory. I can't run fasm ATM Sad


Last edited by revolution on 21 Jan 2020, 08:03; edited 3 times in total
Post 20 Jan 2020, 12:52
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8363
Location: Kraków, Poland
Tomasz Grysztar 20 Jan 2020, 12:53
DimonSoft wrote:
1. Is the limitation of only using numeric expressions in rept by design or just for the purpose of abuse prevention?
Because the syntax of fasm's conditions is "freestyle" (see below) they do not work in any syntactical context other than "all text (after IF/WHILE directive) until the end of line), so it would be tricky to use them with REPT's syntax. Also, because REPT works with numbers, numeric expressions were an obvious choice and other options were not even considered. Note that it originally it was MATCH directive that was supposed to provide conditional preprocessing options, the ability of "REPT 0" to do the same was a side-effect.

DimonSoft wrote:
2. Why were the comparison operators defined as having non-numeric results while still allowing numeric expressions as if conditions?
Allowing numeric expression to act as a simple logical value was a late addition. Originally the design was that condition values were a separate semantic class that always consisted of some kind of "comparison operator" and one or more values to compare. And these values might not even be numeric expressions - operators like EQ, EQTYPE or IN allow to compare arbitrary texts:
Code:
if 'A'{ebx} eqtype ''{al}
        display 'Yup!'
end if    
Later, on request, a special case was added to handle a logical value that contained no comparison operator, a kind of implied/default comparison operator that means "if the value is non-zero".

BTW, the logical value evaluation module of fasmg followed the same design and initially it was also able to handle arbitrary token sequences with EQ. But I later simplified it, because having features like assembly-time MATCH and others made this kind of wild conditions not really necessary. However, as a legacy of fasm 1, fasmg carries over the design of language where conditions (called "logical expressions") are not numeric expressions but a distinct semantic type. Perhaps in the modern world, with majority of popular languages (including even assemblers) being inspired by the syntax of C, this may look strange - but for me this distinction always felt quite natural.
Post 20 Jan 2020, 12:53
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 20 Jan 2020, 13:21
revolution wrote:
You can extend it to any arbitrary block length by not closing the rept

Well, that could be a solution but it introduces yet another special case for reader’s internal {}-matching module. Yes, that could be fixed with a fix directive but I’ve always treated fix as the last resort.

Tomasz Grysztar wrote:
are not numeric expressions but a distinct semantic type. Perhaps in the modern world, with majority of popular languages (including even assemblers) being inspired by the syntax of C, this may look strange - but for me this distinction always felt quite natural.

Let’s just call it a boolean, Pascal-way, for simplicity.

Am I right that the main problem is with the x in <…> syntax? Otherwise it seems to be an easy (and quite useful) addition to FASM to either make comparison operators, C-way, produce numeric values (assembler directives are ready for that) or define, Pascal-way, some way to convert the boolean to a numeric value. The first seems less effort, the second seems better semantically.

I haven’t yet made myself dive into FASM sources, so I might miss some difficulties related to implementation details.
Post 20 Jan 2020, 13:21
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8363
Location: Kraków, Poland
Tomasz Grysztar 20 Jan 2020, 13:53
DimonSoft wrote:
Am I right that the main problem is with the x in <…> syntax?
It is no more problematic that just plain EQ, with which you can do things like "if <...> eq <...>".

DimonSoft wrote:
Otherwise it seems to be an easy (and quite useful) addition to FASM to either make comparison operators, C-way, produce numeric values (assembler directives are ready for that)...
There are additional problems here, because of a short-circuit evaluation, which is not present in fasm's standard RPN-based evaluator for numeric expressions. And fasm actually has some additional optimization in the way it evaluates logical expressions. Note that even the fact that fasm's "&" and "|" operators have the same precedence level was a conscious choice, to allow having a greatly simplified, stack-less short-circuit evaluation. All these simplifications combined would make it a substantial effort to move the evaluation of fasm's logical expressions into the standard numeric evaluator, which is simply not ready to handle them the same way.

DimonSoft wrote:
...or define, Pascal-way, some way to convert the boolean to a numeric value.
The way it would feel the most natural to me would be to have an equivalent of SETC instruction, but for the CALM language (please forgive me, but these days I'm looking at everything through this new lens - on the other hand, CALM is actually just my modern version of fasm's assembly-time bytecode). But honestly, I see little practical use for it, especially when you can make a custom instruction/macro anyway. Smile
Post 20 Jan 2020, 13:53
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 20 Jan 2020, 15:58
Tomasz Grysztar wrote:
fasm's "&" and "|" operators have the same precedence level

Wow! I’ve never thought about that (I prefer to ensure the precedence explicitly for complex conditions). Shouldn’t that be called out in the documentation? It seems like an important difference from most programming languages. I’ve just looked through the FASM.pdf and found no notion of that.
Post 20 Jan 2020, 15:58
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8363
Location: Kraków, Poland
Tomasz Grysztar 20 Jan 2020, 16:08
DimonSoft wrote:
(I prefer to ensure the precedence explicitly for complex conditions).
This is it! It was my impression that most of people seem to do it this way, and it was one of the reasons why I considered this design a good choice (combined with the fact that it allowed for a nice evaluation algorithm). I believe this is because in spoken language we do not really have a "natural" precedence of logical and/or.

This and the fact that fasm's logical expression are separate semantic entities both reflect the fact, that in my designs of programming languages I prefer to see the expressions of logic as a specific way to convey ideas through language, and not just a sub-class of mathematical expressions to be treated in the same unified manner.

DimonSoft wrote:
Shouldn’t that be called out in the documentation? It seems like an important difference from most programming languages. I’ve just looked through the FASM.pdf and found no notion of that.
It has always been openly stated in fasmg's manual, but for fasm 1 apparently it's not, even though I would swear it should have been. Perhaps I forgot that I forgot to put it there. Smile

BTW, this seems to be an evidence for what I wrote above (that usually people ensure precedence explicitly): apparently I forgot to document it, and yet people did not even notice. It's been this way for 20 years! Smile
Post 20 Jan 2020, 16:08
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20489
Location: In your JS exploiting you and your system
revolution 20 Jan 2020, 16:37
Each language has its own special set of precedence rules, so unless you are a super-brain-memory-person no one remembers which precedence has what when they are writing code. So thusly appear the brackets, even if unneeded. It is the safest way IMO.
Post 20 Jan 2020, 16:37
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 20 Jan 2020, 21:34
revolution wrote:
Each language has its own special set of precedence rules, so unless you are a super-brain-memory-person no one remembers which precedence has what when they are writing code. So thusly appear the brackets, even if unneeded. It is the safest way IMO.

Definitely, especially in the world where there’s C with its 16 or 17 levels of precedence, depending on how we count.

Tomasz Grysztar wrote:
BTW, this seems to be an evidence for what I wrote above (that usually people ensure precedence explicitly): apparently I forgot to document it, and yet people did not even notice. It's been this way for 20 years! Smile

Experiment of the century for now!
Post 20 Jan 2020, 21:34
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20489
Location: In your JS exploiting you and your system
revolution 21 Jan 2020, 08:15
Another way to write it is to have the invoker include the curly brackets::
Code:
macro if_le x*, c*, action&
{
  rept (((x) - (c) - 1) and (1 shl 63)) shr 63  action
}
x equ 1
y equ 0
if_le 1, 2, {
 x equ 2
 y equ 4
}

;for a one liner
if_le 1, 2, { x equ 4 }    
Untested.
Post 21 Jan 2020, 08:15
View user's profile Send private message Visit poster's website Reply with quote
Roman



Joined: 21 Apr 2012
Posts: 1872
Roman 21 Jan 2020, 10:17
macro if_le x*, c*, action&
its magick work in Fasm 1.73 ?
Post 21 Jan 2020, 10:17
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20489
Location: In your JS exploiting you and your system
revolution 21 Jan 2020, 10:39
This might also work:
Code:
macro if_le x*, c*, action&
{
  rept (((x) - (c) - 1) and (1 shl 63)) shr 63  action
}
x equ 1
y equ 0
if_le 1, 2 ; <--- no third parameter
{ ;<--- open the rept here
 x equ 2
 y equ 4
}    
Roman: All of these should work in fasm 1.73.
Post 21 Jan 2020, 10:39
View user's profile Send private message Visit poster's website 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.