User Tools

Site Tools


simulating_20assembler_20macros

Simulating assembler macros

by Richard Russell, December 2008

This article supplements the information in the main Help documentation under Conditional assembly and macros.

The assembler built into BBC BASIC for Windows doesn't have a conventional macro facility, but something similar can be simulated utilising the OPT pseudo-instruction. OPT is normally used to control the assembler options (for example whether a listing is generated) but in this application it has two useful features: firstly it generates no code and secondly it takes a numeric parameter. This provides a means of calling a user-defined function from within the assembler, and if that function itself outputs code (or data) it can behave like a macro.

One common use of a macro is to insert a block of code that you use frequently. Suppose your program often wants to find the absolute value of the signed 32-bit number in the eax register, but you don't want the overhead of making a subroutine call to do so. The following code simulates a macro for the purpose:

        DEF FNabs
        [OPT 0
        cdq
        xor eax,edx
        sub eax,edx
        ]
        = pass%

Now, whenever you want to calculate the absolute value of eax you can do so in your main assembler code as follows:

        DIM code% 100, L% -1
        FOR pass% = 8 TO 11 STEP 3
          P% = code%
          [OPT pass%
          ... ; code which puts a signed value into eax
          OPT FNabs
          ... ; more code
          OPT FNabs
          ... ; etc.
          ]
        NEXT pass%
        END

Here FNabs returns the current value of pass%, so in the main assembler loop the OPT FNabs pseudo-instructions simply set the assembler options (to what they already were). However the called function itself emits the opcodes corresponding to the absolute value calculation. You can see the effect by looking at the listing generated (note that the OPT 0 in FNabs prevents any listing being generated there):

1002181A                                OPT pass%
1002181A                                ; code which puts a signed value into eax
1002181A 99 33 C2 2B C2                 OPT FNabs
1002181F                                ; more code
1002181F 99 33 C2 2B C2                 OPT FNabs
10021824                                ; etc.

Another way in which this facility can be used is to extend the assembler to handle instructions which are not supported natively. For example the instruction SYSENTER is available only on the Pentium Pro and later range of processors, and is therefore not supported in the BBC BASIC for Windows assembler. The following user-defined function can be used to 'add' that instruction:

        DEF FNsysenter
        [OPT 0
        DB &0F
        DB &34
        ]
        = pass%

Now, whenever you require to call the SYSENTER instruction, you can do that in your assembler code as follows:

      OPT FNsysenter

A similar technique can be used when the instruction takes a parameter, for example a register name:

        DEF FNfcmovb(reg$)
        [OPT 0
        DB &DA
        DB &C0+VALRIGHT$(reg$)
        ]
        = pass%

This implements the FCMOVB instruction which takes as a parameter an FPU register name (st0 to st7). The routine automatically adjusts the emitted opcode according to the register specified (for simplicity the operand is not checked for validity, but it could be):

      OPT FNfcmovb("st5")

With care the technique can be extended to instructions which take more complex operands.

Using labels in macros


If you need to use labels in the assembler code generated by a macro, care is needed to prevent a Multiple label error occurring when the macro is called more than once. If the labels are genuinely 'local' to the macro (this implies only 'backwards' references) then you can simply declare them as LOCAL:

        DEF FNincarray
        LOCAL label
        [OPT 0
        .label
        inc dword [ebx]
        add ebx,4
        loop label
        ]
        = pass%

Often you can rearrange code to ensure all references to labels are 'backwards' (i.e. the label is defined before the reference to it) but sometimes 'forward references' are unavoidable. In such cases the macro must be adapted so that a different label is created each time it is used. The easiest way of achieving that is to use an array element as the label:

        DEF FNsimplisticabs(i%)
        PRIVATE label()
        LOCAL label
        DIM label(10)
        label = label(i%)
        [OPT pass% AND &E
        test eax,eax
        jns label
        neg eax
        .label
        ret
        ]
        label(i%) = label
        = pass%

Here the macro takes as a parameter an 'instance' value which is different for each use of the macro in your program:

        DIM code% 100, L% -1
        FOR pass% = 8 TO 11 STEP 3
          P% = code%
          [OPT pass%
          ... ; code which puts a signed value into eax
          OPT FNsimplisticabs(0)
          ... ; more code
          OPT FNsimplisticabs(1)
          ... ; etc.
          ]
        NEXT pass%
        END

As shown, the macro can be called up to ten times from the program (0 to 9); if this is insufficient the size of the label() array may be increased.

If it's inconvenient to pass an 'instance' value each time the macro is called (and to ensure they are all different) this may be automated by checking whether the value of pass% has changed, and if so initialising the array subscript:

        DEF FNsimplisticabs
        PRIVATE label(), p%, i%
        LOCAL label
        DIM label(10)
        IF p%<>pass% p% = pass% : i% = 0
        label = label(i%)
        [OPT pass% AND &E
        test eax,eax
        jns label
        neg eax
        .label
        ret
        ]
        label(i%) = label
        i% += 1
        = pass%

Note: Although the interpreter accepts the direct use of array elements as assembler labels, which would allow the above code to be slightly simplified, this is not strictly valid syntax and is not permitted by the compiler.

This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information
simulating_20assembler_20macros.txt · Last modified: 2018/04/13 11:40 by richardrussell