Skip to content

Macro assembler syntax

Alexander V. Chernomyrdin aka chav1961 edited this page Mar 22, 2018 · 11 revisions

Macros and macro language

Macros is an usual mechanism of many different assembler implementations. It uses to produce any parameter-driven pieces of assembler code and insert them into your assembler input stream. The macros, implemented in this assembler, are oriented on maximum macro generation performance. To support it, all the macros you define in your input stream are compiled on-the-fly to Java classes and executed by the JVM directly. To support this ability, all the macro parameters and variables in this implementation of the macro language are strongly typed.

Syntax of the macro language is line-oriented. Every macro operator occupies own separate line in the input stream. A set of macro operators is:

Any other sequences in the lines are treated as text lines and will be simply passed to the output. A special construction &name in the lines is treated as macro substitution and replaces with the current value of the 'name' variable during transmission. Nested (recursive) substitutions are not supported. Name can be either macro parameter or macro local variable name. To separate variable name from the text continuation, a dot(.) can be used:

before&var1&var2.inside&var2&var1.after

If 'var1' value is 100 and 'var2' value is 'test', result of this substitution will be:

before100testinsidetest100after

Data types

Data types supported in the macro assembler are:

  • int - is mapping to Java long
  • real - is mapping to Java double
  • str - is mapping to Java char[]
  • bool - is mapping to Java boolean

Constants of the types are:

  • 123 - int type constant
  • 35.3 and 2.54e-20 - real type constant
  • "abb[p;lkcvl;kdsvdsf-0dvoi\r\n" - str type constant (Java escapes inside double quotest are supported)
  • true and false - bool type constant

.macro operator

This operator is the same first in the macro definition. Every macros can have a set of positional and key parameters. Positional parameters are defined by it's position in the macro parameter list, key parameters are defined by it's unique names in the macro parameters list. All the positional parameters must precede all key parameters in the .macro, positional and key parameters mix is illegal. Syntax of the .macro is:

<.macro> ::= <name> .macro [<positional>[,...]]<key>=[<default>][,...]

<name> ::= <any_Java_compatible_identifier>

<positional> ::= <name>:<type>

<key> ::= <name>:<type>

<default> ::= <constantExpression>

Example of the operator is:

sum		.macro	left:int,right:int,checkOverflow:bool=false
catlist	.macro	source:str,target:str,concat:str,prefix:str=,suffix:str=

This command defines a new macros sum with two positional parameters (with value type int) and one key parameter (with value type bool) and default value false. Positional parameters may not have default values, key parameters can. Macro name must be unique in the given input stream. Parameter names must be unique in the given macro. Default value or the key parameter need be either constant or constant expression to calculate it on the compilation stage.

.local operator

This operator defines new local variable in the macros. Syntax of the .local is:

<.local> ::= <name> .local <type>[=<initialValue>] <initialValue> ::= <constantExpression>

.local operator must immediately follows either .macro or another .local operator only. Local variable name need be unique in the given macro. Initial value need be either constant or constant expression to calculate it on the compilation stage.

Example of the operator is:

myVariable	.local	int
myInitial	.local	str="initial string"

These commands define local variable myVariable (with value type int) with no any initial values and local variable myInitial (with value type *str) with initial value 'initial string'.

.set operator

This is an assignment operator in the macros. Syntax of the .set is:

<.set> ::= <name> .set <expression>

Expression can be any syntactically correct expression. It can contains constants, positional and key parameters, local variables, built-in functions, brackets () and operators.

Complete operator list, ordered by it's priority, is:

operator result conv comment
-N N Y negation
N + N, N - N N Y additions
N * N, N / N, N % N N Y multiplications
X # X S Y concatenation
B ? X : X X Y ternary operation
X < X, X <= X, X > X, X >= X, X == X, X != X B comparison
!B B logical NOT
B && B B logical AND
B || B B logical OR

Letter means:

  • X - any type
  • I - int type
  • R - real type
  • N - any numeric type (int and/or real)
  • S - str type
  • B - bool type
  • Y - automatic data conversion is supported for the given operators
  • y - partially automatic data conversion is supported for the given operators

Complete built-in function list is:

function result type comment
exists(X) B test existent of any value for the variable and parameters only. Any other expression types always return true
int(X) I convert expression to integer
real(X) R convert expression to real
str(X) S convert expression to string
bool(X) B convert expression to bool
uniqueG() I get unique number inside the given assembler input stream
uniqueL() I get unique number inside the given execution of the macro

bool type can be converted to the str type only.

Example of the operator is:

myVar	.set myVar # "123" # (x > 0) ? Y % 20 : 0 # (2 + Z) / 3.5 

Any constant expression will be calculated on the compilation stage. All the variables in the expression must be initialized, otherwise exception will be thrown. Use exists(X) function to check initialization of the variable. Use data conversion function when automatic data conversion is not supported.

.if operator

This operator is conditional operator in the macros. Syntax of the .if is:

<.if> ::= .if <expression1>

. . .

[.elseif <expression2>]

. . .

[.elseif <expressionN>]

. . .

[.else]

. . .

.end

Expression type must be bool only. As .elseif, so .else can be missing. .if operator can be nested.

Example of the operator is:

	.if x==0
		lconst_0
	.elseif x==1
		lconst_1
	.else
		ldc2_w	&x
	.end

.while operator

This operator is the pre-conditional loop operator in the macros. Syntax of the .while is:

<.while> ::= [<label>:] .while <expressison> . . . .end

<label> ::= <name>

Expression type must be bool only. .while operator can be nested. Optional label need be unique in the given macro. It can be used in the .break and .continue operators to break/continue outside the nested loops.

Example of the operator is:

x		.set	10
		.while x > 0
label&x:	nop
x		.set x - 1
		.end

.for operator

This operator is the loop-with-the-variable operator. Syntax of the .for is:

<.for> ::= [<label>:] .for <var> = <initial> to <terminal> [step <step>]

. . .

.end

<initial> ::= <expression>

<terminal> ::= <expression>

<step> ::= <expression>

All expressions need be numeric. Missing step clause defaults 1. Loop variable must be declared by the .local operator. .for operator can be nested. Optional label need be unique in the given macro. It can be used in the .break and .continue operators to break/continue outside the nested loops.

Example of the operator is:

		.for	x = 1 to 10
label&x:	nop
		.end

.choise operator

This operator is analog of the Pascal language case/of operator. Syntax of the .choise is:

<.choise> ::= .choise <expression>

.of <expression1>

. . .

.of <expression2>

. . .

.of <expressionN>

. . .

[.otherwise]

. . .

.end

Expressions can be any sorts (not only constants), but value types of both .choise and .of expressions must be identical. .otherwise clause can be missing. All the .of clauses are mutually exclusive. .choise operator can be nested.

Example of the operator is:

		.choise x
		.of		y
			&x=&x
		.otherwise
			&y=&x
		.end

.error operator

This operator throws chav1961.purelib.basic.exceptions.Calculation exception during macro execution. Syntax of the .error is:

<.error> ::= .error <expression>

Expression can be any string expression. It is passed as parameter to Calculation exception constructor.

Example of the operator is:

		.error	"'X' parameter in not typed on the macro call"

.exit operator

This operator is analog of the return statement in most of languages. Syntax of the .exit is:

<.exit> ::= .exit

.break and .continue operators

These operators are analogs of the break and continue statements in Java. Syntax of them is:

<.break> ::= .break [<label>]

<.continue> ::= .continue [<label>]

Optional label can be any label of the nesting .for/.while operators.

execution of the macro

To execute macro inside your assembler code, simply type it's name and parameters required:

zzz:	sum		2,3				// Macros with posiitonals and missing key
	sum		-100,22,checkOverflow=true	// Macros with positionals and explicitly defined key

Macro call is not strongly required to quote str parameters. You must do it only when your parameter value contains commas (,) or equal signs (=):

		catlist	abc, def, "=3"			// 1-st and 2-nd parameters can be used without quotes
		catlist "abc", "def", "=3"		// ... but can be used with the quotes also
		catlist	a b c, d e f, "=3"		// every parameters is bounded with (,)
		catlist "a b c", "d e f", "=3"		// ... or explicitly with the quotes
		catlist "a,b,c", "d e f", "=3"		//

Any positional parameters can be skipped on the macro call:

	catlist	,def,"=3"				// the same first parameter
	catlist	abc,,"=3"				// the second parameter
	catlist	abc,def					// the same last parameter

Positional parameters skipped will remain uninitialized (exists(...) will return false on it).

Key parameters need follow after all the positional one. It's order is not significant and defines by it's names only:

		calculate		100,200,mul=true,add=true
		calculate		100,200,add=true,mul=true

Key parameters skipped will either remain uninitalized when have no default value (exists(...) will return false on it) or receive default value when have it.

additional remarks

All the macros in the input stream are compiled to Java byte code and stored into the internal repository of the AsmWriter instance. After closing AsmWriter, all the compiled macros inside will be removed. To reuse these macros, you can call special method AsmWriter.clone(OutputStream) to create new AsmWriter instance with the currently defined macros repository. You can also add a set of new macros into the cloned AsmWriter instance. They will be locally defined macros only and they will be removed from everywhere after closing cloned AsmWriter instance. Calling AsmWriter.clone(OutputStream) allow you to build any AsmWriter hierarchy in your program