flat assembler
Message board for the users of flat assembler.

flat assembler > Macroinstructions > Tricky stuff in fasmg, part 4: Generated macros

Author
Thread Post new topic Reply to topic
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6873
Location: Kraków, Poland
This is the fourth part of a series on advanced trickery that can be done with fasmg. The other installments: part 1, part 2, part 3.
_______

As the samples in the previous part demonstrated, it is very easy to make macro generate another macro, and the text of the new one may contain values created by the outer macro or passed from its arguments (and with copied identifier recognition context):
Code:
macro origin o macro offset v dd v - o end macro end macro BASE = 100h origin BASE BASE = 200h offset 220h ; dd 20h
However it is no longer so easy when we need to algorithmically generate a macro that may contain a variable number of lines. Let's say that we need a "generate" macro that could be called like this:
Code:
generate POINT, x dd ?,y dd ?,z dd ?
and it would then define the macro looking like:
Code:
struc POINT xValue:?,yValue:?,zValue:? .x dd xValue .y dd yValue .z dd zValue end struc
We can iterate through the arguments of "generate" and construct the text of the lines we need after deconstructing the arguments with MATCH, however after we start the definition of an inner macro, the iteration is no longer going to work. Any ITERATE or END ITERATE encountered when a macro definition is under way would simply become the part of the macro instead of being immediately processed.

A relatively simple solution that comes to mind, is to generate the text of macro that we need to construct in form of data in a VIRTUAL block, then execute it with EVAL:
Code:
macro generate name,fields& virtual at 0 db 'struc ',`name,' ' iterate field, fields if % > 1 db ',' end if match label= type= default, field db `label,'Value:',`default end match end iterate db 13,10 iterate field, fields match label= type= default, field db '.',`label,' ',`type,' ',`label,'Value',13,10 end match end iterate db 'end struc' local text load text:$ from 0 eval text end virtual end macro
Another variant could achieve the same effect without VIRTUAL, by building the text variable directly with a string concatenation macro (currently there is no built-in string concatenation in fasmg, so this has to be done with bit shifts):
Code:
macro append variable,texts& iterate text, texts variable = string (variable) + (text) shl (lengthof (variable) shl 3) end iterate end macro macro generate name,fields& local text text = 'struc ' append text,`name,' ' iterate field, fields if % > 1 append text,',' end if match label= type= default, field append text,`label,'Value:',`default end match end iterate append text,13,10 iterate field, fields match label= type= default, field append text,'.',`label,' ',`type,' ',`label,'Value',13,10 end match end iterate append text,'end struc' eval text end macro
(temporarily replacing EVAL with DB allows to ensure that the generated text is correct).

But these solutions have some disadvantages. Not only may they be considered wasteful, since they generate a raw text than then needs to be parsed back by the assembler - but the raw text generated in such way can no longer preserve any context for identifiers - this can sometimes be a serious flaw.

The trick that we can use to construct a macro from a variable number of lines that we have prepared as a preprocessed text (which may include some context information) lies in the nested macro calls. Let's look at the following sample:
Code:
macro builder esc macro test end macro macro builder builder db 1 end macro macro builder builder db 2 end macro macro builder builder esc end macro end macro builder
The call to the final "builder" macro initiates the cascade of nested macro calls until it reaches the deepest one which begins the definition of new macro "test". The one after one the nested macros get closed, each one adding an additional line to the macro, until the topmost one is reached which closes the definition with "end macro". The result is definition of this plain and simple macro:
Code:
macro test db 1 db 2 end macro
Before we apply this trick to our problem, we need to realize that we still need EVAL to generate the name of macro parameter, because any parameter is recognized by the preprocessor only when it is a single token. But this EVAL is going to be much shorter and simpler. We are also going to use IRPV to quickly retrieve previously prepared symbolic values into a parameter to generate the line of newly created macro:
Code:
macro append variable,texts& iterate text, texts variable = string (variable) + (text) shl (lengthof (variable) shl 3) end iterate end macro macro generate name,fields& local declarations,argument,line,rmatchN,builder iterate field, fields match label= type= default, field rmatchN = 'rmatch N,' append rmatchN,`label append rmatchN,'Value' eval rmatchN define argument N:default define line label type N end rmatch if % = 1 declarations equ argument else declarations equ declarations,argument end if end match end iterate match arguments, declarations macro builder struc name arguments end macro end match irpv definition, line macro builder builder .#definition end macro end irpv builder end struc end macro
The initial ".#" is attached to the line while defining a "builder" macro, to avoid associating any foreign context with the initial dot, because this dot needs to be interpreted in the context of builded "struc" macro. On the other hand, any contexts for the identifiers in the definitions of the default values are preserved correctly this time.

If we define the building macro as "macro builder!", it is possible to use it to generate only a portion of a macro, because the unconditional macro gets expanded even when it is encountered while definition of a new macro is under way. This allows to re-structure the above sample:
Code:
macro append variable,texts& iterate text, texts variable = string (variable) + (text) shl (lengthof (variable) shl 3) end iterate end macro macro generate name,fields& local declarations,argument,rmatchN,builder macro builder! end macro iterate field, fields match label= type= default, field rmatchN = 'rmatch N,' append rmatchN,`label append rmatchN,'Value' eval rmatchN define argument N:default macro builder! esc builder .#label type N end macro end rmatch if % = 1 declarations equ argument else declarations equ declarations,argument end if end match end iterate match arguments, declarations struc name arguments builder end struc end match end macro
In this variant ESC is used to avoid calling the previous unconditional macro when defining the next one - to create the cascade like in the previous example. But it would still work without ESC and in such case each consecutive macro would contain the entire sequence of prepared lines.
Post 10 Oct 2016, 20:57
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6873
Location: Kraków, Poland
In the Mach-O macros I created to let fasmg assemble itself as MacOS executable I used this simple set of macros based on the above ideas. They allow to build new macro line-by-line with an easy to use syntax:
Code:
define macroBuilder? macro macroBuilder? declaration& macro macroBuilder?.definition esc macro declaration end macro end macro macro macroBuilder?.line? content& macro macroBuilder?.definition macroBuilder?.definition content end macro end macro macro macroBuilder?.end? macroBuilder?.definition esc end macro end macro
For example this:
Code:
macroBuilder test arg iterate symbol, A,B,C,D if % = 1 macroBuilder.line match =symbol?, arg else macroBuilder.line else match =symbol?, arg end if macroBuilder.line display 'Detected symbol ',`symbol,13,10 end iterate macroBuilder.line end match macroBuilder.end
constructs the following macro:
Code:
macro test arg match =A?, arg display 'Detected symbol ','A',13,10 else match =B?, arg display 'Detected symbol ','B',13,10 else match =C?, arg display 'Detected symbol ','C',13,10 else match =D?, arg display 'Detected symbol ','D',13,10 end match end macro
Post 31 Aug 2017, 18:27
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 © 2004-2018, Tomasz Grysztar.

Powered by rwasa.