Note: this post applies the same to fasm 1 as well to fasmg.
Sometimes when constructing a file with fasm it may be needed to reserve an area for a data structures without knowing in advance how much space is going to be needed. For example when manually generating a PE file, it might be initially not known how many sections is it going to contain, but the area for section table needs to be reserved in the header of a file (to be later filled with STORE directive). Fortunately this part of PE file needs to be aligned up to 512 bytes boundary, so even if just a minimum amount is reserved, it is still going to suffice for a few section headers. But if later many sections are defined and it runs out of space, this approach is going to fail.
Forward-referencing allows to easily deal with this kind of problems. It is only needed to count the sections and at the end of source define a constant like:
NUMBER_OF_SECTIONS = SECTION_COUNTER ; with fasmg := may be used to indicate that it is a constant
(this can be put into a POSTPONEd block). And the headers may forward-reference this value to reserve the right amount of space.
This has a small side-effect: it required more passes to be assembled. It is often not noticeable, because other code may require multiple passes as well. But if the forward-referenced value is actually something more fragile that the number of sections, like a value that may keep changing when other parts of file are moving, such reference may end up adding even a several passes. Also, if everything else in the source could assemble just in a one pass, this single forward reference is going to add another.
There is a way to combine the two approaches described above into one. A code may be written in such way that in the first (and sometimes the only one) pass it reserves the "usual" amount that is suspected to be the most often right, and then only if it fails proceed with more passes to resolve the problem. This may look like:
if defined OVERFLOW_SIZE
; reserve as much space as indicated by OVERFLOW_SIZE
else
; reserve the basic size
end if
; at the end of source (or in the POSTPONE):
if REQUIRED_SIZE > BASIC_SIZE
OVERFLOW_SIZE = REQUIRED_SIZE
end if
Thanks to the way in which fasm detects its own mispredictions, this is not going to require a second pass if OVERFLOW_SIZE does not end up getting defined.
The same trick can be used to detect other "unusual" conditions and avoids some special processing early in the source unless something later in the source really ends up requiring it.
This trick is also a demonstration of the intended function of fasm's DEFINED operator. Often it is expected by newcomers that would be akin to #IFDEF of languages like C, but in the case of fasm it serves other important purposes.
A side note: if anyone is wondering - fasm's DEFINED can still be used like #IFDEF, but the symbol needs to be variable and therefore not forward-referable. And fasm only starts treating symbol as a variable when it is defined at least twice:
if ~ defined foo
foo = 1
foo = 1 ; enforce variable status
end if
if ~ defined foo
foo = 2
foo = 2 ; enforce variable status
end if