h1. Contents * [[A BASIC guide to ObjAsm|Introduction & "Hello world!"]] * [[A BASIC guide to ObjAsm Part 2|Structure of ObjAsm source files]] * [[A BASIC guide to ObjAsm Part 3|Constants, literal pools, and structure/workspace definitions]] * Macros and conditional assembly (this page) * [[A BASIC guide to ObjAsm Part 5|Variables and repetitive assembly]] * Multiple source files * objasm command line args * Adding assembler to C programs * ??? * Profit! h1. Conditional assembly ObjAsm has good support for conditional assembly. It's not quite as powerful as BASIC, but it's a lot better than is available with a 'plain' assembler like GNU gas which typically relies on the C preprocessor for implementing conditional assembly and macro functionality. Let's consider the following simple example, which assembles a simple function which copies a block of memory using either byte, halfword, or word transfers. First, the BASIC version: <pre> [ OPT pass ; In: ; R0 -> dest ; R1 -> src ; R2 = length (bytes) .memcpy CMP R2,#0 MOVEQ PC,LR .memcpy_loop ] IF size=1 THEN [ OPT pass LDRB R3,[R1],#1 STRB R3,[R0],#1 SUBS R2,R2,#1 ] ELSE IF size=2 then [ OPT pass LDRH R3,[R1],#2 STRH R3,[R0],#2 SUBS R2,R2,#2 ] ELSE [ OPT pass LDR R3,[R1],#4 STR R3,[R0],#4 SUBS R2,R2,#4 ] ENDIF ENDIF [ OPT pass BNE memcpy_loop MOV PC,LR ] </pre> For ObjAsm, the code would instead look like this: <pre> ; In: ; R0 -> dest ; R1 -> src ; R2 = length (bytes) memcpy CMP R2,#0 MOVEQ PC,LR memcpy_loop [ size = 1 LDRB R3,[R1],#1 STRB R3,[R0],#1 SUBS R2,R2,#1 | [ size = 2 LDRH R3,[R1],#2 STRH R3,[R0],#2 SUBS R2,R2,#2 | LDR R3,[R1],#4 STR R3,[R0],#4 SUBS R2,R2,#4 ] ] BNE memcpy_loop MOV PC,LR </pre> This highlights the three main conditional assembly directives: * @[@ marks the start of a conditional block, and must be followed by an expression. It is functionally equivalent to the @IF ... THEN@ statement in BASIC; if the expression evaluates to true then the block will be assembled. If it is false, it will be skipped. * @|@ is equivalent to ELSE, allowing you to define a block that will be assembled if the expression evaluated to false. As with BASIC, an 'else' clause is optional. * @]@ marks the end of the conditional block. There's also a fourth directive available, @ELIF@, which is equivalent to a single-line @ELSE IF@ in BASIC: It removes the need for an extra @]@ / @ENDIF@ in the block. E.g.: <pre> [ size = 1 LDRB R3,[R1],#1 STRB R3,[R0],#1 SUBS R2,R2,#1 ELIF size = 2 LDRH R3,[R1],#2 STRH R3,[R0],#2 SUBS R2,R2,#2 | LDR R3,[R1],#4 STR R3,[R0],#4 SUBS R2,R2,#4 ] </pre> Note that ObjAsm also accepts IF, ELSE and ENDIF in place of @[@, @|@ and @]@. However in practice the single-character versions are the most widely used. Likewise, it's conventional (at least in the RISC OS sources) to indent any nested conditional assembly directives in proportion to their nesting level. h1. Macros Macros in ObjAsm are a very powerful and capable feature. They allow for extra layers of abstraction to be built ontop of the base instruction set, making complex or frequently-used constructs easier to deal with. In BASIC, you can fake support for macros by using functions or procedures which contain assembler blocks. But ObjAsm's native support for macros makes things much more elegant and convenient. Macros are defined using the @MACRO@ and @MEND@ directives, e.g.: <pre> MACRO $label UpperCase $reg, $wrk $label CMP $reg, #"a" RSBGES $wrk, $reg, #"z" CMPLT $reg, #&e0 RSBGES $wrk, $reg, #&f6 CMPLT $reg, #&f8 RSBGES $wrk, $reg, #&fe SUBGE $reg, $reg, #"a"-"A" MEND </pre> Once defined, they can then be invoked directly as if they were an instruction: <pre> strtoupper LDRB R1,[R0] CMP R1,#0 MOVEQ PC,LR UpperCase R1,R2 STRB R1,[R0],#1 B strtoupper </pre> There are a few key things to take away from the above example: * The first line after the @MACRO@ directive contains the macro name * Macros can take arguments * Arguments are defined on the same line as the macro name, by using identifiers prefixed with '$' * Most of the time the arguments will be on the right of the name, but you can also have an argument on the left of the name. Typically this is used to allow the macro to be placed on the same line as a label definition. In order for this usage to work, you must remember to include the label in the macro body (e.g. on the same line as the first instruction, as with the example above) * ObjAsm places no restriction on the data types used for the arguments. Strings, numbers, register names, (partial) tokens/identifiers, expressions, etc., can all be used as argument values. The only restrictions will be down to how the individual macros handle the arguments (e.g. in the above example, $reg and $wrk are assumed to be registers) The macro body can contain conditional constructs, invocations of other macros, local labels (as discussed in part 1 of the guide), and pretty much anything else that can appear elsewhere in an ObjAsm source file. Macros can also contain optional arguments. For example, the RISC OS sources contain a 6502-inspired @DEC@ macro which has an optional argument: <pre> MACRO $label DEC $reg,$by [ "$by" = "" $label SUB $reg,$reg,#1 | $label SUB $reg,$reg,#$by ] MEND </pre> In this case, the macro body contains a manual check for whether the argument is provided. But modern versions of ObjAsm also allow default values to be specified for arguments, so the above could be simplified to the following: <pre> MACRO $label DEC $reg,$by=1 $label SUB $reg,$reg,#$by MEND </pre> Optional / default arguments can appear anywhere in the argument list. For example, all of the macros provided so far have used @$label@ to allow them to be placed on the same line as a label definition, but specifying a label is not required. Another useful feature of arguments is that you can have an argument which is a suffix on the macro name. Typically this is used to add condition code support to macros, e.g.: <pre> MACRO $label DEC$cc $reg,$by=1 $label SUB$cc $reg,$reg,#$by MEND </pre>