flat assembler
Message board for the users of flat assembler.

Index > Main > Virtual blocks, predictions, oscillation (?): how to fix?

Author
Thread Post new topic Reply to topic
DimonSoft



Joined: 03 Mar 2010
Posts: 1236
Location: Belarus
DimonSoft 05 Sep 2026, 14:24
What I was actually trying to do it to write a set of macros to hide boilerplate stuff while building files of a certain complex format. The format has a fixed-sized section with header having one of several possible forms (of different sizes) and “arbitrary” bytes afterwards (might contain code), up to the maximum size:

Code:
<File data>
...
<Fixed section>
  <Header of variable size (auto-generated with macros)>
  <Arbitrary bytes>
</Fixed section>
...
</File data>    


For modularity my attempt was to hold both the <Fixed section> and <Arbitrary bytes> in virtual-blocks, and then copy bytes from one to another and to the final FASM output.

I’ve managed to reduce the PREPSRC output to the following:
Code:
ORIGIN equ $81

virtual at MyLabel

  jmp Error
Error:

; !!! Comment and uncomment this line !!!
;  db 98 dup $01

if~($-ORIGIN<=100)
  'Data is too large.'
end if

; Display the number of bytes left until the fixed section limit is reached
value?03=(100-($-ORIGIN))
if value?03<0
display '-'
value?03=-value?03
end if
sum?07=0
divisor?05=10'000'000'000'000'000'000
repeat 20
digit?06=(value?03/divisor?05)mod 10
divisor?05=divisor?05/10
if digit?06>0
display '0'+digit?06
else if sum?07|%=20
display '0'
end if
sum?07=sum?07+digit?06
end repeat

end virtual

virtual at ORIGIN
MyLabel:
end virtual    


When the line marked with !!! comment is commented, the code below prints the number of bytes left — 98. Now when I add 98 more bytes to fill the fixed section exactly till the end, the 'Data is too large' message appears although everything should fit just fine.

When ORIGIN is less than $81, the problem disappears, which might be caused by the jmp encoding (?).

I feel, this is somehow caused by the FASM value prediction algorithm, but I can’t understand how to fix the code to avoid the problem.
Post 05 Sep 2026, 14:24
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: 21091
Location: In your JS exploiting you and your system
revolution 05 Sep 2026, 15:44
The final virtual block is not needed. Change the first "virtual at MyLabel" to "virtual at ORIGIN" and delete the last three lines.

Alternatively if for some reason the extra virtual block is needed then move it up to the second line:
Code:
ORIGIN equ $81

virtual at ORIGIN
MyLabel:
end virtual

virtual at MyLabel

  jmp Error
Error:

; !!! Comment and uncomment this line !!!
 db 98 dup $01

if~($-ORIGIN<=100)
  'Data is too large.'
end if

; Display the number of bytes left until the fixed section limit is reached
value?03=(100-($-ORIGIN))
if value?03<0
display '-'
value?03=-value?03
end if
sum?07=0
divisor?05=10'000'000'000'000'000'000
repeat 20
digit?06=(value?03/divisor?05)mod 10
divisor?05=divisor?05/10
if digit?06>0
display '0'+digit?06
else if sum?07|%=20
display '0'
end if
sum?07=sum?07+digit?06
end repeat

end virtual    
Post 05 Sep 2026, 15:44
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1236
Location: Belarus
DimonSoft 05 Sep 2026, 16:07
Sorry, I might have reduced the code a bit too much.

In real code the second block contains some bytes before MyLabel (the variable-sized header), and after MyLabel the first virtual block contents are copied. So, the ORIGIN value is not really equal to MyLabel, MyLabel is greater by some value.

Updated piece of code:
Code:
ORIGIN equ $81

virtual at MyLabel
  vbFirst::

  jmp Error
Error:

; !!! Comment and uncomment this line !!!
;  db 98 dup $01

if~($-ORIGIN<=100)
  'Data is too large.'
end if

; Display the number of bytes left until the fixed section limit is reached
value?03=(100-($-ORIGIN))
if value?03<0
display '-'
value?03=-value?03
end if
sum?07=0
divisor?05=10'000'000'000'000'000'000
repeat 20
digit?06=(value?03/divisor?05)mod 10
divisor?05=divisor?05/10
if digit?06>0
display '0'+digit?06
else if sum?07|%=20
display '0'
end if
sum?07=sum?07+digit?06
end repeat

end virtual

virtual at ORIGIN
db 'Some bytes here'
MyLabel:

repeat ...
; Copying bytes from vbFirst
end repeat
end virtual    


So, removing the second block would require additional calculations instead of just relying on FASM to resolve it.

The first version of the code makes the problem visible better by removing the details.
Post 05 Sep 2026, 16:07
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: 21091
Location: In your JS exploiting you and your system
revolution 05 Sep 2026, 18:12
fasm is correct. The data is more than 100 bytes. The output shows -15 bytes remaining, i.e. 15 bytes too much.
Post 05 Sep 2026, 18:12
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: 21091
Location: In your JS exploiting you and your system
revolution 05 Sep 2026, 18:15
Maybe using $$ instead of ORIGIN is what is needed?
Code:
ORIGIN equ $81

virtual at ORIGIN
db 'Some bytes here'
MyLabel:

;repeat ...
; Copying bytes from vbFirst
;end repeat
end virtual

virtual at MyLabel
  vbFirst::

  jmp Error
Error:

; !!! Comment and uncomment this line !!!
  db 98 dup $01

if~($-$$<=100)
  'Data is too large.'
end if

; Display the number of bytes left until the fixed section limit is reached
value?03=(100-($-$$))
if value?03<0
display '-'
value?03=-value?03
end if
sum?07=0
divisor?05=10'000'000'000'000'000'000
repeat 20
digit?06=(value?03/divisor?05)mod 10
divisor?05=divisor?05/10
if digit?06>0
display '0'+digit?06
else if sum?07|%=20
display '0'
end if
sum?07=sum?07+digit?06
end repeat

end virtual    
Code:
flat assembler  version 1.73.31  (16384 kilobytes memory)
0
2 passes, 0 bytes.    
Post 05 Sep 2026, 18:15
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1236
Location: Belarus
DimonSoft 05 Sep 2026, 20:10
As far as I understand, $$ is the origin of the closest nested virtual block, so in this case the suggested check is that vbFirst has at most 100 bytes. While the original check is that vbFirst will end before or at 100 bytes from ORIGIN-block, no matter what data prepends it in that block.

In the example with db 'Some bytes here' vbFirst should be limited to 100–15=85 bytes total, so that its last byte is no further than 100 bytes from ORIGIN.

What really surprises me is that removing the if block with 'Data is too large' message with no other changes just make things work, except that the check is desired. Looks like depending on the presence of the check FASM calculates different offsets. I really can’t see how and why that happens.
Post 05 Sep 2026, 20:10
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1236
Location: Belarus
DimonSoft 05 Sep 2026, 20:16
revolution wrote:
fasm is correct. The data is more than 100 bytes. The output shows -15 bytes remaining, i.e. 15 bytes too much.

It consists of “jmp +0” (2 bytes, right?) and 98 bytes produces by db.

Now note that original code didn’t have the db 'Some bytes here' (so vbFirst data actually should have ended at 100 bytes offset in outer block), but still the check fired the error message.
Post 05 Sep 2026, 20:16
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: 21091
Location: In your JS exploiting you and your system
revolution 06 Sep 2026, 01:33
DimonSoft wrote:
revolution wrote:
fasm is correct. The data is more than 100 bytes. The output shows -15 bytes remaining, i.e. 15 bytes too much.

It consists of “jmp +0” (2 bytes, right?) and 98 bytes produces by db.
Plus the extra "db 'Some bytes here'" to give the extra 15 bytes.
Post 06 Sep 2026, 01:33
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1236
Location: Belarus
DimonSoft 06 Sep 2026, 05:33
revolution wrote:
DimonSoft wrote:
revolution wrote:
fasm is correct. The data is more than 100 bytes. The output shows -15 bytes remaining, i.e. 15 bytes too much.

It consists of “jmp +0” (2 bytes, right?) and 98 bytes produces by db.
Plus the extra "db 'Some bytes here'" to give the extra 15 bytes.

Only in the extended case. Remove the db and note that it’s still ”too large” in spite of being exactly 100 bytes.
Post 06 Sep 2026, 05:33
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: 21091
Location: In your JS exploiting you and your system
revolution 06 Sep 2026, 05:49
DimonSoft wrote:
revolution wrote:
DimonSoft wrote:
revolution wrote:
fasm is correct. The data is more than 100 bytes. The output shows -15 bytes remaining, i.e. 15 bytes too much.

It consists of “jmp +0” (2 bytes, right?) and 98 bytes produces by db.
Plus the extra "db 'Some bytes here'" to give the extra 15 bytes.

Only in the extended case. Remove the db and note that it’s still ”too large” in spite of being exactly 100 bytes.
If the computation is from ORIGIN then it also includes all bytes after ORIGIN. Even a single extra byte after ORIGIN will be included in the calculation. So I'm not sure what the problem is here. fasm correctly says the distance is more than 100 bytes from ORIGIN to the measurement point.
Post 06 Sep 2026, 05:49
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1236
Location: Belarus
DimonSoft 06 Sep 2026, 06:12
revolution wrote:
DimonSoft wrote:
revolution wrote:
DimonSoft wrote:
revolution wrote:
fasm is correct. The data is more than 100 bytes. The output shows -15 bytes remaining, i.e. 15 bytes too much.

It consists of “jmp +0” (2 bytes, right?) and 98 bytes produces by db.
Plus the extra "db 'Some bytes here'" to give the extra 15 bytes.

Only in the extended case. Remove the db and note that it’s still ”too large” in spite of being exactly 100 bytes.
If the computation is from ORIGIN then it also includes all bytes after ORIGIN. Even a single extra byte after ORIGIN will be included in the calculation. So I'm not sure what the problem is here. fasm correctly says the distance is more than 100 bytes from ORIGIN to the measurement point.

Please, move back to the code in the original post. The extended version was given just to show why I’m not willing to remove one of the virtual blocks.

In the original code everything fits just fine, but the error message is still shown.
Post 06 Sep 2026, 06:12
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: 21091
Location: In your JS exploiting you and your system
revolution 06 Sep 2026, 06:52
Oh, so the problem is the error message itself? Malformed instructions can't be resolved with multiple passes, so fasm will error out immediately as soon as it sees one.

That can be fixed by using a resolvable error:
Code:
  rb -1 ; 'Data is too large.' ; <--- rb -1 can be resolved with multi-pass    
Post 06 Sep 2026, 06:52
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1236
Location: Belarus
DimonSoft 06 Sep 2026, 07:40
Well, not exactly the error message but the fact that it prevents FASM from doing the correct prediction and, yes, causes the compilation to fail immediately.

If I get it right, you suggest using something that is syntactically valid but causes error if the branch is finally selected. That might work but leaves another problem unsolved: the readable error message.

For now the best way I found to get exactly the error message in the tiny FASMW error window is to put it in code as plain string literal, this ensures the message is shown complete and without other junk (like, say, err directive). rb -1 is not really useful for someone who uses the large macro set.
Post 06 Sep 2026, 07:40
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: 21091
Location: In your JS exploiting you and your system
revolution 06 Sep 2026, 07:50
Then I think fasmw needs to be updated to show better outputs for errors. Because multi-pass resolution can give false values for forward referenced labels, so trying to always have if tests be correct in the presence of unknown future values is not possible in the general case. There is always the possibility that the if test will be wrong on an intermediate pass, but correct on the final pass.

fasm shows then entire line, including the comments and other trailing stuff.
Post 06 Sep 2026, 07:50
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: 21091
Location: In your JS exploiting you and your system
revolution 06 Sep 2026, 08:39
Note there is also assert. It still has the same problem with the comment, but is a bit tidier than using if.
Code:
assert $-ORIGIN<=100 ; 'Data is too large.'    
Post 06 Sep 2026, 08:39
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1236
Location: Belarus
DimonSoft 06 Sep 2026, 09:32
Actually, in the project itself the check is implemented as an assert macro that overrides the directive to allow custom error message Smile
Post 06 Sep 2026, 09:32
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-2026, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.